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 2018 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Support functions for smb2_ioctl/fsctl codes:
  18  * FSCTL_SRV_OFFLOAD_READ
  19  * FSCTL_SRV_OFFLOAD_WRITE
  20  * (and related)
  21  */
  22 
  23 #include <smbsrv/smb2_kproto.h>
  24 #include <smbsrv/smb_fsops.h>
  25 #include <smb/winioctl.h>
  26 
  27 /*
  28  * Summary of how offload data transfer works:
  29  *
  30  * The client drives a server-side copy.  Outline:
  31  * 1: open src_file
  32  * 2: create dst_file and set its size
  33  * 3: while src_file not all copied {
  34  *        offload_read(src_file, &token);
  35  *        while token not all copied {
  36  *            offload_write(dst_file, token);
  37  *        }
  38  *    }
  39  *
  40  * Each "offload read" request returns a "token" representing some
  41  * portion of the source file.  The server decides what kind of
  42  * token to use, and how much of the source file it should cover.
  43  * The length represented may be less then the client requested.
  44  * No data are copied during offload_read (just meta-data).
  45  *
  46  * Each "offload write" request copies some portion of the data
  47  * represented by the "token" into the output file.  The amount
  48  * of data copied may be less than the client requested, and the
  49  * client keeps sending offload write requests until they have
  50  * copied all the data represented by the current token.
  51  */
  52 
  53 /* [MS-FSA] OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND_CURRENT_RANGE */
  54 #define OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND       1
  55 
  56 /*
  57  * [MS-FSCC] 2.3.79 STORAGE_OFFLOAD_TOKEN
  58  * Note reserved: 0xFFFF0002 – 0xFFFFFFFF
  59  *
  60  * ...TOKEN_TYPE_ZERO_DATA:  A well-known Token that indicates ...
  61  * (offload write should just zero to the destination)
  62  * The payload (tok_other) is ignored with this type.
  63  */
  64 #define STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA    0xFFFF0001
  65 
  66 /* Our vendor-specific token type: struct tok_native1 */
  67 #define STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1      0x10001
  68 
  69 #define TOKEN_TOTAL_SIZE        512
  70 #define TOKEN_MAX_PAYLOAD       504     /* 512 - 8 */
  71 
  72 /* This mask is for sanity checking offsets etc. */
  73 #define OFFMASK         ((uint64_t)DEV_BSIZE-1)
  74 
  75 typedef struct smb_odx_token {
  76         uint32_t        tok_type;       /* big-endian on the wire */
  77         uint16_t        tok_reserved;   /* zero */
  78         uint16_t        tok_len;        /* big-endian on the wire */
  79         union {
  80                 uint8_t u_tok_other[TOKEN_MAX_PAYLOAD];
  81                 struct tok_native1 {
  82                         smb2fid_t       tn1_fid;
  83                         uint64_t        tn1_off;
  84                         uint64_t        tn1_eof;
  85                 } u_tok_native1;
  86         } tok_u;
  87 } smb_odx_token_t;
  88 
  89 typedef struct odx_write_args {
  90         uint32_t in_struct_size;
  91         uint32_t in_flags;
  92         uint64_t in_dstoff;
  93         uint64_t in_xlen;
  94         uint64_t in_xoff;
  95         uint32_t out_struct_size;
  96         uint32_t out_flags;
  97         uint64_t out_xlen;
  98         uint64_t wa_eof;
  99 } odx_write_args_t;
 100 
 101 static int smb_odx_get_token(mbuf_chain_t *, smb_odx_token_t *);
 102 static int smb_odx_get_token_native1(mbuf_chain_t *, struct tok_native1 *);
 103 static int smb_odx_put_token(mbuf_chain_t *, smb_odx_token_t *);
 104 static int smb_odx_put_token_native1(mbuf_chain_t *, struct tok_native1 *);
 105 
 106 static uint32_t smb2_fsctl_odx_write_zeros(smb_request_t *, odx_write_args_t *);
 107 static uint32_t smb2_fsctl_odx_write_native1(smb_request_t *,
 108     odx_write_args_t *, smb_odx_token_t *);
 109 
 110 
 111 /* We can disable this feature for testing etc. */
 112 int smb2_odx_enable = 1;
 113 
 114 /*
 115  * These two variables determine the intervals of offload_read and
 116  * offload_write calls (respectively) during an offload copy.
 117  *
 118  * For the offload read token we could offer a token representing
 119  * the whole file, but we'll have the client come back for a new
 120  * "token" after each 256M so we have a chance to look for "holes".
 121  * This lets us use the special "zero" token while we're in any
 122  * un-allocated parts of the file, so offload_write can use the
 123  * (more efficient) smb_fsop_freesp instead of copying.
 124  *
 125  * We limit the size of offload_write to 16M per request so we
 126  * don't end up taking so long with I/O that the client might
 127  * time out the request.  Keep: write_max <= read_max
 128  */
 129 uint32_t smb2_odx_read_max = (1<<28); /* 256M */
 130 uint32_t smb2_odx_write_max = (1<<24); /* 16M */
 131 
 132 /*
 133  * This buffer size determines the I/O size for the copy during
 134  * offoad write, where it will read/write using this buffer.
 135  * Note: We kmem_alloc this, so don't make it HUGE.  It only
 136  * needs to be large enough to allow the copy to proceed with
 137  * reasonable efficiency.  1M is currently the largest possible
 138  * block size with ZFS, so that's what we'll use here.
 139  */
 140 uint32_t smb2_odx_buf_size = (1<<20); /* 1M */
 141 
 142 
 143 /*
 144  * FSCTL_OFFLOAD_READ
 145  * [MS-FSCC] 2.3.77
 146  *
 147  * Similar (in concept) to FSCTL_SRV_REQUEST_RESUME_KEY
 148  *
 149  * The returned data is an (opaque to the client) 512-byte "token"
 150  * that represents the specified range (offset, length) of the
 151  * source file.  The "token" we return here comes back to us in an
 152  * FSCTL_OFFLOAD_READ.  We must stash whatever we'll need then in
 153  * the token we return here.
 154  *
 155  * We want server-side copy to be able to copy "holes" efficiently,
 156  * but would rather avoid the complexity of encoding a list of all
 157  * allocated ranges into our returned token, so this compromise:
 158  *
 159  * When the current range is entirely within a "hole", we'll return
 160  * the special "zeros" token, and the offload write using that token
 161  * will use the simple and very efficient smb_fsop_freesp.  In this
 162  * scenario, we'll have a copy stride of smb2_odx_read_max (256M).
 163  *
 164  * When there's any data in the range to copy, we'll return our
 165  * "native" token, and the subsequent offload_write will walk the
 166  * allocated ranges copying and/or zeroing as needed.  In this
 167  * scenario, we'll have a copy stride of smb2_odx_write_max (16M).
 168  *
 169  * One additional optimization allowed by the protocol is that when
 170  * we discover that there's no more data after the current range,
 171  * we can set the flag ..._ALL_ZERO_BEYOND which tells that client
 172  * they can stop copying here if they like.
 173  */
 174 uint32_t
 175 smb2_fsctl_odx_read(smb_request_t *sr, smb_fsctl_t *fsctl)
 176 {
 177         smb_attr_t src_attr;
 178         smb_odx_token_t *tok = NULL;
 179         struct tok_native1 *tn1;
 180         smb_ofile_t *ofile = sr->fid_ofile;
 181         uint64_t src_size, src_rnd_size;
 182         off64_t data, hole;
 183         uint32_t in_struct_size;
 184         uint32_t in_flags;
 185         uint32_t in_ttl;
 186         uint64_t in_file_off;
 187         uint64_t in_copy_len;
 188         uint64_t out_xlen;
 189         uint32_t out_struct_size = TOKEN_TOTAL_SIZE + 16;
 190         uint32_t out_flags = 0;
 191         uint32_t status;
 192         uint32_t tok_type;
 193         int rc;
 194 
 195         if (smb2_odx_enable == 0)
 196                 return (NT_STATUS_NOT_SUPPORTED);
 197 
 198         /*
 199          * Make sure the (src) ofile granted access allows read.
 200          * [MS-FSA] didn't mention this, so it's not clear where
 201          * this should happen relative to other checks.  Usually
 202          * access checks happen early.
 203          */
 204         status = smb_ofile_access(ofile, ofile->f_cr, FILE_READ_DATA);
 205         if (status != NT_STATUS_SUCCESS)
 206                 return (status);
 207 
 208         /*
 209          * Decode FSCTL_OFFLOAD_READ_INPUT struct,
 210          * and do in/out size checks.
 211          */
 212         rc = smb_mbc_decodef(
 213             fsctl->in_mbc, "lll4.qq",
 214             &in_struct_size,        /* l */
 215             &in_flags,              /* l */
 216             &in_ttl,                /* l */
 217             /* reserved         4. */
 218             &in_file_off,   /* q */
 219             &in_copy_len);  /* q */
 220         if (rc != 0)
 221                 return (NT_STATUS_BUFFER_TOO_SMALL);
 222         if (fsctl->MaxOutputResp < out_struct_size)
 223                 return (NT_STATUS_BUFFER_TOO_SMALL);
 224 
 225         /*
 226          * More arg checking per MS-FSA
 227          */
 228         if ((in_file_off & OFFMASK) != 0 ||
 229             (in_copy_len & OFFMASK) != 0)
 230                 return (NT_STATUS_INVALID_PARAMETER);
 231         if (in_struct_size != 32)
 232                 return (NT_STATUS_INVALID_PARAMETER);
 233         if (in_file_off > INT64_MAX ||
 234             (in_file_off + in_copy_len) < in_file_off)
 235                 return (NT_STATUS_INVALID_PARAMETER);
 236 
 237         /*
 238          * [MS-FSA] (summarizing)
 239          * If not data stream, or if sparse, encrypted, compressed...
 240          * return STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED.
 241          *
 242          * We'll ignore most of those except to require:
 243          * Plain file, not a stream.
 244          */
 245         if (!smb_node_is_file(ofile->f_node))
 246                 return (NT_STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED);
 247         if (SMB_IS_STREAM(ofile->f_node))
 248                 return (NT_STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED);
 249 
 250         /*
 251          * [MS-FSA] If Open.Stream.IsDeleted ...
 252          * We don't really have this.
 253          */
 254 
 255         /*
 256          * If CopyLength == 0, "return immediately success".
 257          */
 258         if (in_copy_len == 0) {
 259                 out_xlen = 0;
 260                 tok_type = STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA;
 261                 goto done;
 262         }
 263 
 264         /*
 265          * Check for lock conflicting with the read.
 266          */
 267         status = smb_lock_range_access(sr, ofile->f_node,
 268             in_file_off, in_copy_len, B_FALSE);
 269         if (status != 0)
 270                 return (status); /* == FILE_LOCK_CONFLICT */
 271 
 272         /*
 273          * Get the file size (rounded to a full block)
 274          * and check the requested offset.
 275          */
 276         bzero(&src_attr, sizeof (src_attr));
 277         src_attr.sa_mask = SMB_AT_SIZE;
 278         status = smb2_ofile_getattr(sr, ofile, &src_attr);
 279         if (status != NT_STATUS_SUCCESS)
 280                 return (status);
 281         src_size = src_attr.sa_vattr.va_size;
 282         if (in_file_off >= src_size)
 283                 return (NT_STATUS_END_OF_FILE);
 284 
 285         /*
 286          * Limit the transfer length based on (rounded) EOF.
 287          * Clients expect ranges of whole disk blocks.
 288          * If we get a read in this rounded-up range,
 289          * we'll supply zeros.
 290          */
 291         src_rnd_size = (src_size + OFFMASK) & ~OFFMASK;
 292         out_xlen = in_copy_len;
 293         if ((in_file_off + out_xlen) > src_rnd_size)
 294                 out_xlen = src_rnd_size - in_file_off;
 295 
 296         /*
 297          * Also, have the client come back for a new token after every
 298          * smb2_odx_read_max bytes, so we'll have opportunities to
 299          * recognize "holes" in the source file.
 300          */
 301         if (out_xlen > smb2_odx_read_max)
 302                 out_xlen = smb2_odx_read_max;
 303 
 304         /*
 305          * Ask the filesystem if there are any allocated regions in
 306          * the requested range, and return either the "zeros" token
 307          * or our "native" token as appropriate (details above).
 308          */
 309         data = in_file_off;
 310         tok_type = STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1;
 311         rc = smb_fsop_next_alloc_range(ofile->f_cr, ofile->f_node,
 312             &data, &hole);
 313         switch (rc) {
 314         case 0:
 315                 /* Found some data.  Is it beyond this range? */
 316                 if (data >= (in_file_off + out_xlen))
 317                         tok_type = STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA;
 318                 break;
 319         case ENXIO:
 320                 /* No data here or following. */
 321                 tok_type = STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA;
 322                 out_flags |= OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND;
 323                 break;
 324         case ENOSYS:    /* FS does not support VOP_IOCTL... */
 325         case ENOTTY:    /* ... or _FIO_SEEK_DATA, _HOLE */
 326                 break;
 327         default:
 328                 cmn_err(CE_NOTE, "smb_fsop_next_alloc_range: rc=%d", rc);
 329                 break;
 330         }
 331 
 332 done:
 333         /* Already checked MaxOutputResp */
 334         (void) smb_mbc_encodef(
 335             fsctl->out_mbc, "llq",
 336             out_struct_size,    /* l */
 337             out_flags,          /* l */
 338             out_xlen);          /* q */
 339 
 340         /*
 341          * Build the ODX token to return
 342          */
 343         tok = smb_srm_zalloc(sr, sizeof (*tok));
 344         tok->tok_type = tok_type;
 345         tok->tok_reserved = 0;
 346         if (tok_type == STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1) {
 347                 tok->tok_len = sizeof (*tn1);
 348                 tn1 = &tok->tok_u.u_tok_native1;
 349                 tn1->tn1_fid.persistent = ofile->f_persistid;
 350                 tn1->tn1_fid.temporal = ofile->f_fid;
 351                 tn1->tn1_off = in_file_off;
 352                 tn1->tn1_eof = src_size;
 353         }
 354 
 355         rc = smb_odx_put_token(fsctl->out_mbc, tok);
 356         if (rc != 0)
 357                 return (NT_STATUS_BUFFER_TOO_SMALL);
 358 
 359         return (NT_STATUS_SUCCESS);
 360 }
 361 
 362 /*
 363  * FSCTL_SRV_OFFLOAD_WRITE
 364  * [MS-FSCC] 2.3.80
 365  *
 366  * Similar (in concept) to FSCTL_COPYCHUNK_WRITE
 367  *
 368  * Copies from a source file identified by a "token"
 369  * (previously returned by FSCTL_OFFLOAD_READ)
 370  * to the file on which the ioctl is issued.
 371  */
 372 uint32_t
 373 smb2_fsctl_odx_write(smb_request_t *sr, smb_fsctl_t *fsctl)
 374 {
 375         smb_attr_t dst_attr;
 376         odx_write_args_t args;
 377         smb_odx_token_t *tok = NULL;
 378         smb_ofile_t *ofile = sr->fid_ofile;
 379         uint64_t dst_rnd_size;
 380         uint32_t status = NT_STATUS_INVALID_PARAMETER;
 381         int rc;
 382 
 383         bzero(&args, sizeof (args));
 384         args.out_struct_size = 16;
 385 
 386         if (smb2_odx_enable == 0)
 387                 return (NT_STATUS_NOT_SUPPORTED);
 388 
 389         /*
 390          * Make sure the (dst) ofile granted_access allows write.
 391          * [MS-FSA] didn't mention this, so it's not clear where
 392          * this should happen relative to other checks.  Usually
 393          * access checks happen early.
 394          */
 395         status = smb_ofile_access(ofile, ofile->f_cr, FILE_WRITE_DATA);
 396         if (status != NT_STATUS_SUCCESS)
 397                 return (status);
 398 
 399         /*
 400          * Decode FSCTL_OFFLOAD_WRITE_INPUT struct,
 401          * and do in/out size checks.
 402          */
 403         rc = smb_mbc_decodef(
 404             fsctl->in_mbc, "llqqq",
 405             &args.in_struct_size,   /* l */
 406             &args.in_flags,         /* l */
 407             &args.in_dstoff,                /* q */
 408             &args.in_xlen,          /* q */
 409             &args.in_xoff);         /* q */
 410         if (rc != 0)
 411                 return (NT_STATUS_BUFFER_TOO_SMALL);
 412         tok = smb_srm_zalloc(sr, sizeof (*tok));
 413         rc = smb_odx_get_token(fsctl->in_mbc, tok);
 414         if (rc != 0)
 415                 return (NT_STATUS_BUFFER_TOO_SMALL);
 416         if (fsctl->MaxOutputResp < args.out_struct_size)
 417                 return (NT_STATUS_BUFFER_TOO_SMALL);
 418 
 419         /*
 420          * More arg checking per MS-FSA
 421          */
 422         if ((args.in_dstoff & OFFMASK) != 0 ||
 423             (args.in_xoff & OFFMASK) != 0 ||
 424             (args.in_xlen & OFFMASK) != 0)
 425                 return (NT_STATUS_INVALID_PARAMETER);
 426         if (args.in_struct_size != (TOKEN_TOTAL_SIZE + 32))
 427                 return (NT_STATUS_INVALID_PARAMETER);
 428         if (args.in_dstoff > INT64_MAX ||
 429             (args.in_dstoff + args.in_xlen) < args.in_dstoff)
 430                 return (NT_STATUS_INVALID_PARAMETER);
 431 
 432         /*
 433          * If CopyLength == 0, "return immediately success".
 434          */
 435         if (args.in_xlen == 0) {
 436                 status = 0;
 437                 goto done;
 438         }
 439 
 440         /*
 441          * [MS-FSA] (summarizing)
 442          * If not data stream, or if sparse, encrypted, compressed...
 443          * return STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED.
 444          *
 445          * We'll ignore most of those except to require:
 446          * Plain file, not a stream.
 447          */
 448         if (!smb_node_is_file(ofile->f_node))
 449                 return (NT_STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED);
 450         if (SMB_IS_STREAM(ofile->f_node))
 451                 return (NT_STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED);
 452 
 453         /*
 454          * [MS-FSA] If Open.Stream.IsDeleted ...
 455          * We don't really have such a thing.
 456          * Also skip Volume.MaxFileSize check.
 457          */
 458 
 459         /*
 460          * Check for lock conflicting with the write.
 461          */
 462         status = smb_lock_range_access(sr, ofile->f_node,
 463             args.in_dstoff, args.in_xlen, B_TRUE);
 464         if (status != 0)
 465                 return (status); /* == FILE_LOCK_CONFLICT */
 466 
 467         /*
 468          * Need the file size
 469          */
 470         bzero(&dst_attr, sizeof (dst_attr));
 471         dst_attr.sa_mask = SMB_AT_SIZE;
 472         status = smb2_ofile_getattr(sr, ofile, &dst_attr);
 473         if (status != NT_STATUS_SUCCESS)
 474                 return (status);
 475         args.wa_eof = dst_attr.sa_vattr.va_size;
 476         dst_rnd_size = (args.wa_eof + OFFMASK) & ~OFFMASK;
 477 
 478         /*
 479          * Destination offset vs. EOF
 480          */
 481         if (args.in_dstoff >= args.wa_eof)
 482                 return (NT_STATUS_END_OF_FILE);
 483 
 484         /*
 485          * Destination offset+len vs. EOF
 486          *
 487          * The spec. is silent about copying when the file length is
 488          * not block aligned, but clients appear to ask us to copy a
 489          * range that's rounded up to a block size.  We'll limit the
 490          * transfer size to the rounded up file size, but the actual
 491          * copy will stop at EOF (args.wa_eof).
 492          */
 493         if ((args.in_dstoff + args.in_xlen) > dst_rnd_size)
 494                 args.in_xlen = dst_rnd_size - args.in_dstoff;
 495 
 496         /*
 497          * Finally, run the I/O
 498          */
 499         switch (tok->tok_type) {
 500         case STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA:
 501                 status = smb2_fsctl_odx_write_zeros(sr, &args);
 502                 break;
 503         case STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1:
 504                 status = smb2_fsctl_odx_write_native1(sr, &args, tok);
 505                 break;
 506         default:
 507                 status = NT_STATUS_INVALID_TOKEN;
 508                 break;
 509         }
 510 
 511 done:
 512         /*
 513          * Checked MaxOutputResp above, so we can ignore errors
 514          * from mbc_encodef here.
 515          */
 516         if (status == NT_STATUS_SUCCESS) {
 517                 (void) smb_mbc_encodef(
 518                     fsctl->out_mbc, "llq",
 519                     args.out_struct_size,
 520                     args.out_flags,
 521                     args.out_xlen);
 522         }
 523 
 524         return (status);
 525 }
 526 
 527 /*
 528  * Handle FSCTL_OFFLOAD_WRITE with token type
 529  * STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA
 530  *
 531  * In this handler, the "token" represents a source of zeros.
 532  */
 533 static uint32_t
 534 smb2_fsctl_odx_write_zeros(smb_request_t *sr, odx_write_args_t *args)
 535 {
 536         smb_ofile_t *dst_ofile = sr->fid_ofile;
 537         uint64_t xlen = args->in_xlen;
 538         uint32_t status = 0;
 539         int rc;
 540 
 541         ASSERT(args->in_xlen > 0);
 542 
 543         /*
 544          * Limit the I/O size.  In here we're just doing freesp,
 545          * which is assumed to require only meta-data I/O, so
 546          * we'll allow up to smb2_odx_read_max (256M) per call.
 547          * This is essentially just a double-check of the range
 548          * we gave the client at the offload_read call, making
 549          * sure they can't use a zero token for longer ranges
 550          * than offload_read would allow.
 551          */
 552         if (xlen > smb2_odx_read_max)
 553                 xlen = smb2_odx_read_max;
 554 
 555         /*
 556          * Also limit to the actual file size, which may be
 557          * smaller than the (block-aligned) transfer size.
 558          * Report the rounded up size to the caller at EOF.
 559          */
 560         args->out_xlen = xlen;
 561         if ((args->in_dstoff + xlen) > args->wa_eof)
 562                 xlen = args->wa_eof - args->in_dstoff;
 563 
 564         /*
 565          * Arrange for zeros to appear in the range:
 566          * in_dstoff, (in_dstoff + in_xlen)
 567          *
 568          * Just "free" the range and let it allocate as needed
 569          * when someone later writes in this range.
 570          */
 571         rc = smb_fsop_freesp(sr, dst_ofile->f_cr, dst_ofile,
 572             args->in_dstoff, xlen);
 573         if (rc != 0) {
 574                 status = smb_errno2status(rc);
 575                 if (status == NT_STATUS_INVALID_PARAMETER ||
 576                     status == NT_STATUS_NOT_SUPPORTED)
 577                         status = NT_STATUS_INVALID_DEVICE_REQUEST;
 578                 args->out_xlen = 0;
 579         } else {
 580                 status = 0;
 581         }
 582 
 583         return (status);
 584 }
 585 
 586 /*
 587  * Handle FSCTL_OFFLOAD_WRITE with token type
 588  * STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1
 589  */
 590 static uint32_t
 591 smb2_fsctl_odx_write_native1(smb_request_t *sr,
 592     odx_write_args_t *args, smb_odx_token_t *tok)
 593 {
 594         struct tok_native1 *tn1;
 595         smb_ofile_t *dst_ofile = sr->fid_ofile;
 596         smb_ofile_t *src_ofile = NULL;
 597         void *buffer = NULL;
 598         size_t bufsize = smb2_odx_buf_size;
 599         uint64_t src_offset;
 600         uint32_t resid;
 601         uint32_t xlen;
 602         uint32_t status;
 603 
 604         /*
 605          * Lookup the source ofile using the resume key,
 606          * which smb2_fsctl_offload_read encoded as an
 607          * smb2fid_t.  Similar to smb2sr_lookup_fid(),
 608          * but different error code.
 609          */
 610         tn1 = &tok->tok_u.u_tok_native1;
 611         src_ofile = smb_ofile_lookup_by_fid(sr,
 612             (uint16_t)tn1->tn1_fid.temporal);
 613         if (src_ofile == NULL ||
 614             src_ofile->f_persistid != tn1->tn1_fid.persistent) {
 615                 status = NT_STATUS_INVALID_TOKEN;
 616                 goto out;
 617         }
 618 
 619         /*
 620          * Make sure src_ofile is open on a regular file, and
 621          * granted access includes READ_DATA
 622          */
 623         if (!smb_node_is_file(src_ofile->f_node)) {
 624                 status = NT_STATUS_ACCESS_DENIED;
 625                 goto out;
 626         }
 627         status = smb_ofile_access(src_ofile, src_ofile->f_cr, FILE_READ_DATA);
 628         if (status != NT_STATUS_SUCCESS)
 629                 goto out;
 630 
 631         /*
 632          * Limit the I/O size.  In here we're actually copying,
 633          * so limit to smb2_odx_write_max (16M) per call.
 634          * Note that xlen is a 32-bit value here.
 635          */
 636         if (args->in_xlen > smb2_odx_write_max)
 637                 xlen = smb2_odx_write_max;
 638         else
 639                 xlen = (uint32_t)args->in_xlen;
 640 
 641         /*
 642          * Also limit to the actual file size, which may be
 643          * smaller than the (block-aligned) transfer size.
 644          * Report the rounded up size to the caller at EOF.
 645          */
 646         args->out_xlen = xlen;
 647         if ((args->in_dstoff + xlen) > args->wa_eof)
 648                 xlen = (uint32_t)(args->wa_eof - args->in_dstoff);
 649 
 650         /*
 651          * Note: in_xoff is relative to the beginning of the "token"
 652          * (a range of the source file tn1_off, tn1_eof).  Make sure
 653          * in_xoff is within the range represented by this token.
 654          */
 655         src_offset = tn1->tn1_off + args->in_xoff;
 656         if (src_offset >= tn1->tn1_eof ||
 657             src_offset < tn1->tn1_off) {
 658                 status = NT_STATUS_INVALID_PARAMETER;
 659                 goto out;
 660         }
 661 
 662         /*
 663          * Get a buffer used for copying, always
 664          * smb2_odx_buf_size (1M)
 665          *
 666          * Rather than sleep for this relatively large allocation,
 667          * allow the allocation to fail and return an error.
 668          * The client should then fall back to normal copy.
 669          */
 670         buffer = kmem_alloc(bufsize, KM_NOSLEEP_LAZY);
 671         if (buffer == NULL) {
 672                 status = NT_STATUS_INSUFF_SERVER_RESOURCES;
 673                 goto out;
 674         }
 675 
 676         /*
 677          * Copy src to dst for xlen
 678          */
 679         resid = xlen;
 680         status = smb2_sparse_copy(sr, src_ofile, dst_ofile,
 681             src_offset, args->in_dstoff, &resid, buffer, bufsize);
 682 
 683         /*
 684          * If the result was a partial copy, round down the
 685          * reported transfer size to a block boundary.
 686          */
 687         if (resid != 0) {
 688                 xlen -= resid;
 689                 xlen &= ~OFFMASK;
 690                 args->out_xlen = xlen;
 691         }
 692 
 693         /*
 694          * If we did any I/O, ignore the error that stopped us.
 695          * We'll report this error during the next call.
 696          */
 697         if (args->out_xlen > 0)
 698                 status = 0;
 699 
 700 out:
 701         if (src_ofile != NULL)
 702                 smb_ofile_release(src_ofile);
 703 
 704         if (buffer != NULL)
 705                 kmem_free(buffer, bufsize);
 706 
 707         return (status);
 708 }
 709 
 710 /*
 711  * Get an smb_odx_token_t from the (input) mbuf chain.
 712  * Consumes exactly TOKEN_TOTAL_SIZE bytes.
 713  */
 714 static int
 715 smb_odx_get_token(mbuf_chain_t *mbc, smb_odx_token_t *tok)
 716 {
 717         mbuf_chain_t tok_mbc;
 718         int start_pos = mbc->chain_offset;
 719         int rc;
 720 
 721         if (MBC_ROOM_FOR(mbc, TOKEN_TOTAL_SIZE) == 0)
 722                 return (-1);
 723 
 724         /*
 725          * No big-endian support in smb_mbc_encodef, so swap
 726          * the big-endian fields: tok_type (32-bits),
 727          * (reserved is 16-bit zero, so no swap),
 728          * and tok_len (16-bits)
 729          */
 730         rc = smb_mbc_decodef(
 731             mbc, "l..w",
 732             &tok->tok_type,
 733             /* tok_reserved */
 734             &tok->tok_len);
 735         if (rc != 0)
 736                 return (rc);
 737         tok->tok_type = BSWAP_32(tok->tok_type);
 738         tok->tok_len = BSWAP_16(tok->tok_len);
 739 
 740         if (tok->tok_len > TOKEN_MAX_PAYLOAD)
 741                 return (-1);
 742         rc = MBC_SHADOW_CHAIN(&tok_mbc, mbc,
 743             mbc->chain_offset, tok->tok_len);
 744         if (rc != 0)
 745                 return (rc);
 746 
 747         switch (tok->tok_type) {
 748         case STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA:
 749                 /* no payload */
 750                 break;
 751         case STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1:
 752                 rc = smb_odx_get_token_native1(&tok_mbc,
 753                     &tok->tok_u.u_tok_native1);
 754                 break;
 755         default:
 756                 /* caller will error out */
 757                 break;
 758         }
 759 
 760         if (rc == 0) {
 761                 /* Advance past what we shadowed. */
 762                 mbc->chain_offset = start_pos + TOKEN_TOTAL_SIZE;
 763         }
 764 
 765         return (rc);
 766 }
 767 
 768 static int
 769 smb_odx_get_token_native1(mbuf_chain_t *mbc, struct tok_native1 *tn1)
 770 {
 771         int rc;
 772 
 773         rc = smb_mbc_decodef(
 774             mbc, "qqqq",
 775             &tn1->tn1_fid.persistent,
 776             &tn1->tn1_fid.temporal,
 777             &tn1->tn1_off,
 778             &tn1->tn1_eof);
 779 
 780         return (rc);
 781 }
 782 
 783 /*
 784  * Put an smb_odx_token_t into the (output) mbuf chain,
 785  * padded to TOKEN_TOTAL_SIZE bytes.
 786  */
 787 static int
 788 smb_odx_put_token(mbuf_chain_t *mbc, smb_odx_token_t *tok)
 789 {
 790         int rc, padlen;
 791         int start_pos = mbc->chain_offset;
 792         int end_pos = start_pos + TOKEN_TOTAL_SIZE;
 793 
 794         if (tok->tok_len > TOKEN_MAX_PAYLOAD)
 795                 return (-1);
 796 
 797         /*
 798          * No big-endian support in smb_mbc_encodef, so swap
 799          * the big-endian fields: tok_type (32-bits),
 800          * (reserved is 16-bit zero, so no swap),
 801          * and tok_len (16-bits)
 802          */
 803         rc = smb_mbc_encodef(
 804             mbc, "lww",
 805             BSWAP_32(tok->tok_type),
 806             0, /* tok_reserved */
 807             BSWAP_16(tok->tok_len));
 808         if (rc != 0)
 809                 return (rc);
 810 
 811         switch (tok->tok_type) {
 812         case STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA:
 813                 /* no payload */
 814                 break;
 815         case STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1:
 816                 rc = smb_odx_put_token_native1(mbc,
 817                     &tok->tok_u.u_tok_native1);
 818                 break;
 819         default:
 820                 ASSERT(0);
 821                 return (-1);
 822         }
 823 
 824         /* Pad out to TOKEN_TOTAL_SIZE bytes. */
 825         if (mbc->chain_offset < end_pos) {
 826                 padlen = end_pos - mbc->chain_offset;
 827                 (void) smb_mbc_encodef(mbc, "#.", padlen);
 828         }
 829         ASSERT(mbc->chain_offset == end_pos);
 830 
 831         return (rc);
 832 }
 833 
 834 static int
 835 smb_odx_put_token_native1(mbuf_chain_t *mbc, struct tok_native1 *tn1)
 836 {
 837         int rc;
 838 
 839         rc = smb_mbc_encodef(
 840             mbc, "qqqq",
 841             tn1->tn1_fid.persistent,
 842             tn1->tn1_fid.temporal,
 843             tn1->tn1_off,
 844             tn1->tn1_eof);
 845 
 846         return (rc);
 847 }