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_SET_SPARSE
  19  * FSCTL_SET_ZERO_DATA
  20  * FSCTL_QUERY_ALLOCATED_RANGES
  21  */
  22 
  23 #include <smbsrv/smb2_kproto.h>
  24 #include <smbsrv/smb_fsops.h>
  25 #include <smb/winioctl.h>
  26 
  27 /*
  28  * FSCTL_SET_SPARSE
  29  *
  30  * In args: one byte flag (optional: default TRUE)
  31  */
  32 uint32_t
  33 smb2_fsctl_set_sparse(smb_request_t *sr, smb_fsctl_t *fsctl)
  34 {
  35         smb_attr_t attr;
  36         smb_ofile_t *ofile = sr->fid_ofile;
  37         cred_t *kcr;
  38         uint32_t amask;
  39         uint32_t status;
  40         uint8_t flag;
  41         int rc;
  42 
  43         rc = smb_mbc_decodef(fsctl->in_mbc, "b", &flag);
  44         if (rc != 0)
  45                 flag = 0xff;
  46 
  47         if (!smb_node_is_file(ofile->f_node))
  48                 return (NT_STATUS_INVALID_PARAMETER);
  49 
  50         /*
  51          * Allow if we have any of FILE_WRITE_ATTRIBUTES,
  52          * FILE_WRITE_DATA, FILE_APPEND_DATA
  53          */
  54         amask = FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_APPEND_DATA;
  55         if ((ofile->f_granted_access & amask) == 0)
  56                 return (NT_STATUS_ACCESS_DENIED);
  57 
  58         /*
  59          * Need the current DOS attributes
  60          */
  61         bzero(&attr, sizeof (attr));
  62         attr.sa_mask = SMB_AT_DOSATTR;
  63         kcr = zone_kcred();
  64         status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
  65         if (status != NT_STATUS_SUCCESS)
  66                 return (status);
  67 
  68         if (flag != 0) {
  69                 /* Set "sparse" */
  70                 if (attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)
  71                         return (0);
  72                 attr.sa_dosattr |= FILE_ATTRIBUTE_SPARSE_FILE;
  73         } else {
  74                 /* Clear "sparse" */
  75                 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0)
  76                         return (0);
  77                 attr.sa_dosattr &= ~FILE_ATTRIBUTE_SPARSE_FILE;
  78         }
  79 
  80         attr.sa_mask = SMB_AT_DOSATTR;
  81         status = smb_node_setattr(sr, ofile->f_node, kcr, ofile, &attr);
  82         return (status);
  83 }
  84 
  85 /*
  86  * FSCTL_SET_ZERO_DATA
  87  *
  88  * In args: uint64_t start_off, end_off
  89  */
  90 uint32_t
  91 smb2_fsctl_set_zero_data(smb_request_t *sr, smb_fsctl_t *fsctl)
  92 {
  93         smb_attr_t attr;
  94         smb_ofile_t *ofile = sr->fid_ofile;
  95         uint64_t start_off, end_off, zero_len;
  96         uint32_t status;
  97         int rc;
  98 
  99         rc = smb_mbc_decodef(fsctl->in_mbc, "qq",
 100             &start_off, &end_off);
 101         if (rc != 0)
 102                 return (NT_STATUS_BUFFER_TOO_SMALL);
 103 
 104         /*
 105          * The given offsets are actually int64_t (signed).
 106          */
 107         if (start_off > INT64_MAX ||
 108             end_off > INT64_MAX ||
 109             start_off > end_off)
 110                 return (NT_STATUS_INVALID_PARAMETER);
 111 
 112         if (!smb_node_is_file(ofile->f_node))
 113                 return (NT_STATUS_INVALID_PARAMETER);
 114 
 115         /*
 116          * This operation is effectively a write (of zeros)
 117          */
 118         status = smb_ofile_access(ofile, ofile->f_cr, FILE_WRITE_DATA);
 119         if (status != NT_STATUS_SUCCESS)
 120                 return (status);
 121 
 122         /*
 123          * Need the file size
 124          */
 125         bzero(&attr, sizeof (attr));
 126         attr.sa_mask = SMB_AT_SIZE;
 127         status = smb_node_getattr(sr, ofile->f_node, ofile->f_cr,
 128             ofile, &attr);
 129         if (status != NT_STATUS_SUCCESS)
 130                 return (status);
 131 
 132         /*
 133          * Ignore any zero-ing beyond EOF
 134          */
 135         if (end_off > attr.sa_vattr.va_size)
 136                 end_off = attr.sa_vattr.va_size;
 137         if (start_off >= end_off)
 138                 return (0);
 139         zero_len = end_off - start_off;
 140 
 141         /*
 142          * Check for lock conflicting with the write.
 143          */
 144         status = smb_lock_range_access(sr, ofile->f_node,
 145             start_off, zero_len, B_TRUE);
 146         if (status != 0)
 147                 return (status); /* == FILE_LOCK_CONFLICT */
 148 
 149         rc = smb_fsop_freesp(sr, ofile->f_cr, ofile,
 150             start_off, zero_len);
 151         if (rc != 0)
 152                 status = smb_errno2status(rc);
 153 
 154         return (status);
 155 }
 156 
 157 /*
 158  * FSCTL_QUERY_ALLOCATED_RANGES
 159  *
 160  * Incoming args: uint64_t start_off, end_off
 161  */
 162 struct alloc_range {
 163         off64_t off;
 164         off64_t len;
 165 };
 166 uint32_t
 167 smb2_fsctl_query_alloc_ranges(smb_request_t *sr, smb_fsctl_t *fsctl)
 168 {
 169         smb_attr_t attr;
 170         cred_t *kcr;
 171         smb_ofile_t *ofile = sr->fid_ofile;
 172         struct alloc_range arg, res;
 173         off64_t cur_off, end_off;
 174         uint32_t status;
 175         int err, rc;
 176 
 177         /*
 178          * Most ioctls return NT_STATUS_BUFFER_TOO_SMALL for
 179          * short in/out buffers, but for this one, MS-FSA
 180          * says short input returns invalid parameter.
 181          */
 182         rc = smb_mbc_decodef(fsctl->in_mbc, "qq", &arg.off, &arg.len);
 183         if (rc != 0)
 184                 return (NT_STATUS_INVALID_PARAMETER);
 185 
 186         /*
 187          * The given offsets are actually int64_t (signed).
 188          */
 189         end_off = arg.off + arg.len;
 190         if (arg.off > INT64_MAX || arg.len < 0 ||
 191             end_off > INT64_MAX || end_off < arg.off)
 192                 return (NT_STATUS_INVALID_PARAMETER);
 193 
 194         if (!smb_node_is_file(ofile->f_node))
 195                 return (NT_STATUS_INVALID_PARAMETER);
 196 
 197         /*
 198          * This operation is effectively a read
 199          */
 200         status = smb_ofile_access(ofile, ofile->f_cr, FILE_READ_DATA);
 201         if (status != NT_STATUS_SUCCESS)
 202                 return (status);
 203 
 204         if (arg.len == 0) {
 205                 /* MS-FSA says empty result for this. */
 206                 return (0);
 207         }
 208 
 209         /*
 210          * Need the file size and dosattr
 211          */
 212         bzero(&attr, sizeof (attr));
 213         attr.sa_mask = SMB_AT_SIZE | SMB_AT_DOSATTR;
 214         kcr = zone_kcred();
 215         status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
 216         if (status != NT_STATUS_SUCCESS)
 217                 return (status);
 218         if (end_off > attr.sa_vattr.va_size)
 219                 end_off = attr.sa_vattr.va_size;
 220 
 221         /*
 222          * Only sparse files should present un-allocated ranges.
 223          * If non-sparse, MS-FSA says (just return one range).
 224          */
 225         if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
 226                 if (arg.off < end_off) {
 227                         res.off = arg.off;
 228                         res.len = end_off - arg.off;
 229                         rc = smb_mbc_encodef(fsctl->out_mbc, "qq",
 230                             res.off, res.len);
 231                         if (rc != 0)
 232                                 return (NT_STATUS_BUFFER_TOO_SMALL);
 233                 }
 234                 return (0);
 235         }
 236 
 237         cur_off = arg.off;
 238         while (cur_off < end_off) {
 239                 off64_t data, hole;
 240 
 241                 data = cur_off;
 242                 err = smb_fsop_next_alloc_range(kcr, ofile->f_node,
 243                     &data, &hole);
 244                 if (err != 0)
 245                         break;
 246 
 247                 /* sanity check data (ensure progress) */
 248                 if (data < cur_off) {
 249                         ASSERT(0);
 250                         data = cur_off;
 251                 }
 252 
 253                 /* Normal termination */
 254                 if (data >= end_off)
 255                         break;
 256 
 257                 /* sanity check hole (ensure progress) */
 258                 if (hole <= data)
 259                         hole = end_off;
 260 
 261                 /* Trim this range as needed. */
 262                 if (hole > end_off)
 263                         hole = end_off;
 264 
 265                 res.off = data;
 266                 res.len = hole - data;
 267 
 268                 if (res.len > 0) {
 269                         rc = smb_mbc_encodef(fsctl->out_mbc, "qq",
 270                             res.off, res.len);
 271                         if (rc != 0)
 272                                 return (NT_STATUS_BUFFER_TOO_SMALL);
 273                 }
 274 
 275                 cur_off = hole;
 276         }
 277 
 278         return (0);
 279 }
 280 
 281 /*
 282  * Copy a segment of a file, preserving sparseness.
 283  * Uses a caller-provided buffer for read/write.
 284  * Caller should already have checked for locks.
 285  *
 286  * On entry, *residp is the length to copy.
 287  * On return, it's the "resid" (amount not copied)
 288  *
 289  * If this gets an error from any I/O, return it, even if some data
 290  * have already been copied.  The caller should normally ignore an
 291  * error when some data have been copied.
 292  */
 293 uint32_t
 294 smb2_sparse_copy(
 295         smb_request_t *sr,
 296         smb_ofile_t *src_ofile, smb_ofile_t *dst_ofile,
 297         off64_t src_off, off64_t dst_off, uint32_t *residp,
 298         void *buffer, size_t bufsize)
 299 {
 300         iovec_t iov;
 301         uio_t uio;
 302         off64_t data, hole;
 303         uint32_t xfer;
 304         uint32_t status = 0;
 305         int rc;
 306 
 307         while (*residp > 0) {
 308 
 309                 data = src_off;
 310                 rc = smb_fsop_next_alloc_range(src_ofile->f_cr,
 311                     src_ofile->f_node, &data, &hole);
 312                 switch (rc) {
 313                 case 0:
 314                         /* Found data, hole */
 315                         break;
 316                 case ENXIO:
 317                         /* No data after here (will skip below). */
 318                         data = hole = (src_off + *residp);
 319                         break;
 320                 default:
 321                         cmn_err(CE_NOTE,
 322                             "smb_fsop_next_alloc_range: rc=%d", rc);
 323                         /* FALLTHROUGH */
 324                 case ENOSYS:    /* FS does not support VOP_IOCTL... */
 325                 case ENOTTY:    /* ... or _FIO_SEEK_DATA, _HOLE */
 326                         data = src_off;
 327                         hole = src_off + *residp;
 328                         break;
 329                 }
 330 
 331                 /*
 332                  * Don't try to go past (src_off + *residp)
 333                  */
 334                 if (hole > (src_off + *residp))
 335                         hole = src_off + *residp;
 336                 if (data > hole)
 337                         data = hole;
 338 
 339                 /*
 340                  * If there's a gap (src_off .. data)
 341                  * skip in src_ofile, zero in dst_ofile
 342                  */
 343                 if (src_off < data) {
 344                         off64_t skip = data - src_off;
 345                         rc = smb_fsop_freesp(sr, dst_ofile->f_cr,
 346                             dst_ofile, dst_off, skip);
 347                         if (rc == 0) {
 348                                 src_off += skip;
 349                                 dst_off += skip;
 350                                 *residp -= (uint32_t)skip;
 351                         } else {
 352                                 /* Fall back to regular copy */
 353                                 data = src_off;
 354                         }
 355                 }
 356                 ASSERT(src_off == data);
 357 
 358                 /*
 359                  * Copy this segment: src_off .. hole
 360                  */
 361                 while (src_off < hole) {
 362                         ssize_t tsize = hole - src_off;
 363                         if (tsize > bufsize)
 364                                 tsize = bufsize;
 365 
 366                         /*
 367                          * Read src_ofile into buffer
 368                          */
 369                         iov.iov_base = buffer;
 370                         iov.iov_len  = tsize;
 371                         bzero(&uio, sizeof (uio));
 372                         uio.uio_iov = &iov;
 373                         uio.uio_iovcnt = 1;
 374                         uio.uio_resid = tsize;
 375                         uio.uio_loffset = src_off;
 376                         uio.uio_segflg = UIO_SYSSPACE;
 377                         uio.uio_extflg = UIO_COPY_DEFAULT;
 378 
 379                         rc = smb_fsop_read(sr, src_ofile->f_cr,
 380                             src_ofile->f_node, src_ofile, &uio, 0);
 381                         if (rc != 0) {
 382                                 status = smb_errno2status(rc);
 383                                 return (status);
 384                         }
 385                         /* Note: Could be partial read. */
 386                         tsize -= uio.uio_resid;
 387                         ASSERT(tsize > 0);
 388 
 389                         /*
 390                          * Write buffer to dst_ofile
 391                          */
 392                         iov.iov_base = buffer;
 393                         iov.iov_len  = tsize;
 394                         bzero(&uio, sizeof (uio));
 395                         uio.uio_iov = &iov;
 396                         uio.uio_iovcnt = 1;
 397                         uio.uio_resid = tsize;
 398                         uio.uio_loffset = dst_off;
 399                         uio.uio_segflg = UIO_SYSSPACE;
 400                         uio.uio_extflg = UIO_COPY_DEFAULT;
 401 
 402                         rc = smb_fsop_write(sr, dst_ofile->f_cr,
 403                             dst_ofile->f_node, dst_ofile, &uio, &xfer, 0);
 404                         if (rc != 0) {
 405                                 status = smb_errno2status(rc);
 406                                 return (status);
 407                         }
 408                         ASSERT(xfer <= tsize);
 409 
 410                         src_off += xfer;
 411                         dst_off += xfer;
 412                         *residp -= xfer;
 413                 }
 414                 ASSERT(src_off == hole);
 415         }
 416 
 417         return (status);
 418 }
 419 
 420 /*
 421  * Not sure what header this might go in.
 422  */
 423 #define FILE_REGION_USAGE_VALID_CACHED_DATA     1
 424 #define FILE_REGION_USAGE_VALID_NONCACHED_DATA  2
 425 #define FILE_REGION_USAGE_VALID__MASK           3
 426 
 427 typedef struct _FILE_REGION_INFO {
 428         uint64_t off;
 429         uint64_t len;
 430         uint32_t usage;
 431         uint32_t reserved;
 432 } FILE_REGION_INFO;
 433 
 434 
 435 /*
 436  * FSCTL_QUERY_FILE_REGIONS
 437  *
 438  * [MS-FSCC] 2.3.39 FSCTL_QUERY_FILE_REGIONS Request
 439  * [MS-FSCC] 2.3.40.1 FILE_REGION_INFO
 440  *
 441  * Looks like Hyper-V uses this to query the "valid data length",
 442  * which to us is the beginning offset of the last "hole".
 443  * Similar logic as smb2_sparse_copy()
 444  */
 445 uint32_t
 446 smb2_fsctl_query_file_regions(smb_request_t *sr, smb_fsctl_t *fsctl)
 447 {
 448         smb_attr_t attr;
 449         cred_t *kcr;
 450         smb_ofile_t *ofile = sr->fid_ofile;
 451         FILE_REGION_INFO arg;
 452         off64_t cur_off, end_off, eof;
 453         off64_t data, hole;
 454         uint32_t tot_regions, put_regions;
 455         uint32_t status;
 456         int rc;
 457 
 458         if (fsctl->InputCount == 0) {
 459                 arg.off = 0;
 460                 arg.len = INT64_MAX;
 461                 arg.usage = FILE_REGION_USAGE_VALID_CACHED_DATA;
 462                 arg.reserved = 0;
 463         } else {
 464                 /* min size check: reserved is optional */
 465                 rc = smb_mbc_decodef(fsctl->in_mbc, "qql",
 466                     &arg.off, &arg.len, &arg.usage);
 467                 if (rc != 0)
 468                         return (NT_STATUS_BUFFER_TOO_SMALL);
 469 
 470                 /*
 471                  * The given offset and length are int64_t (signed).
 472                  */
 473                 if (arg.off > INT64_MAX || arg.len > INT64_MAX)
 474                         return (NT_STATUS_INVALID_PARAMETER);
 475                 if ((arg.off + arg.len) > INT64_MAX)
 476                         return (NT_STATUS_INVALID_PARAMETER);
 477                 if ((arg.usage & FILE_REGION_USAGE_VALID__MASK) == 0)
 478                         return (NT_STATUS_INVALID_PARAMETER);
 479                 arg.reserved = 0;
 480         }
 481 
 482         if (fsctl->MaxOutputResp < (16 + sizeof (FILE_REGION_INFO)))
 483                 return (NT_STATUS_BUFFER_TOO_SMALL);
 484 
 485         if (!smb_node_is_file(ofile->f_node))
 486                 return (NT_STATUS_INVALID_PARAMETER);
 487 
 488         /*
 489          * This operation is effectively a read
 490          */
 491         status = smb_ofile_access(ofile, ofile->f_cr, FILE_READ_DATA);
 492         if (status != NT_STATUS_SUCCESS)
 493                 return (status);
 494 
 495         /*
 496          * Need the file size and dosattr
 497          */
 498         bzero(&attr, sizeof (attr));
 499         attr.sa_mask = SMB_AT_SIZE | SMB_AT_DOSATTR;
 500         kcr = zone_kcred();
 501         status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
 502         if (status != NT_STATUS_SUCCESS)
 503                 return (status);
 504 
 505         cur_off = arg.off;
 506         end_off = arg.off + arg.len;
 507         eof = attr.sa_vattr.va_size;
 508 
 509         /*
 510          * If (InputRegion.FileOffset > Eof) OR
 511          * ((InputRegion.FileOffset == Eof) AND (Eof > 0)),
 512          * the operation MUST return STATUS_SUCCESS, with
 513          * BytesReturned set to 0 (empty data response)
 514          */
 515         if ((arg.off > eof) || (arg.off == eof && eof > 0))
 516                 return (NT_STATUS_SUCCESS);
 517         if (end_off > eof)
 518                 end_off = eof;
 519 
 520         /*
 521          * We're going to return at least one region.  Put place-holder
 522          * data for the fixed part of the response.  Will overwrite this
 523          * later, when we know how many regions there are and how many
 524          * of those fit in the allowed response buffer space.  These are:
 525          * Flags, TotalRegionEntryCount, RegionEntryCount, Reserved
 526          */
 527         rc = smb_mbc_encodef(fsctl->out_mbc, "llll", 0, 0, 0, 0);
 528         if (rc != 0)
 529                 return (NT_STATUS_BUFFER_TOO_SMALL);
 530         tot_regions = put_regions = 0;
 531 
 532         /*
 533          * Get the first pair of (data, hole) offsets at or after
 534          * the current offset (cur_off).
 535          */
 536         data = hole = cur_off;
 537         rc = smb_fsop_next_alloc_range(ofile->f_cr,
 538             ofile->f_node, &data, &hole);
 539         switch (rc) {
 540         case 0:
 541                 /* Found (data, hole) */
 542                 break;
 543         case ENXIO:
 544                 /* No more data after cur_off. */
 545                 break;
 546         default:
 547                 cmn_err(CE_NOTE, "smb_fsop_next_alloc_range: rc=%d", rc);
 548                 /* FALLTHROUGH */
 549         case ENOSYS:    /* FS does not support VOP_IOCTL... */
 550         case ENOTTY:    /* ... or _FIO_SEEK_DATA, _HOLE */
 551                 data = cur_off;
 552                 hole = eof;
 553                 break;
 554         }
 555         DTRACE_PROBE2(range0, uint64_t, data, uint64_t, hole);
 556 
 557         /*
 558          * Only sparse files should present un-allocated regions.
 559          * If non-sparse, MS-FSA says to just return one region.
 560          * There still can be a "hole" but only one, starting at
 561          * "valid data length" (VDL) and ending at end of file.
 562          * To determine VDL, find the last (data,hole) pair, then
 563          * VDL is the last "hole" offset.  Note that the above
 564          * smb_fsop_next_alloc_range may have set data somewhere
 565          * above cur_off, so we we have to reset that here.
 566          */
 567         if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
 568                 /*
 569                  * This works, but it's rather inefficient, and
 570                  * usually just finds VDL==EOF.  Should look into
 571                  * whether there's a faster way to find the VDL.
 572                  */
 573 #if 0
 574                 off64_t next_data, next_hole;
 575                 data = cur_off;
 576                 do {
 577                         next_data = next_hole = hole;
 578                         rc = smb_fsop_next_alloc_range(ofile->f_cr,
 579                             ofile->f_node, &next_data, &next_hole);
 580                         if (rc == 0) {
 581                                 hole = next_hole;
 582                         }
 583                 } while (rc == 0);
 584 #else
 585                 /* Assume no "holes" anywhere (VDL==EOF) */
 586                 data = cur_off;
 587                 hole = eof;
 588 #endif
 589                 DTRACE_PROBE2(range1, uint64_t, data, uint64_t, hole);
 590         }
 591 
 592         /*
 593          * Loop terminates in the middle, continuing
 594          * while (cur_off < end_off)
 595          */
 596         for (;;) {
 597                 /*
 598                  * We have a data region that covers (data, hole).
 599                  * It could be partially or entirely beyond the range
 600                  * the caller asked about (if so trim it).
 601                  */
 602                 if (hole > end_off)
 603                         hole = end_off;
 604                 if (data > hole)
 605                         data = hole;
 606 
 607                 /*
 608                  * If cur_off < data encode a "hole" region
 609                  * (cur_off,data) and advance cur_off.
 610                  */
 611                 if (cur_off < data) {
 612                         rc = smb_mbc_encodef(fsctl->out_mbc, "qqll",
 613                             cur_off,
 614                             (data - cur_off),
 615                             0, // usage (hole)
 616                             0); // reserved
 617                         cur_off = data;
 618                         if (rc == 0)
 619                                 put_regions++;
 620                         tot_regions++;
 621                 }
 622 
 623                 /*
 624                  * If cur_off < hole encode a "data" region
 625                  * (cur_off,hole) and advance cur_off.
 626                  */
 627                 if (cur_off < hole) {
 628                         rc = smb_mbc_encodef(fsctl->out_mbc, "qqll",
 629                             cur_off,
 630                             (hole - cur_off),
 631                             FILE_REGION_USAGE_VALID_CACHED_DATA,
 632                             0); // reserved
 633                         cur_off = hole;
 634                         if (rc == 0)
 635                                 put_regions++;
 636                         tot_regions++;
 637                 }
 638 
 639                 /*
 640                  * Normal loop termination
 641                  */
 642                 if (cur_off >= end_off)
 643                         break;
 644 
 645                 /*
 646                  * Get the next region (data, hole) starting on or after
 647                  * the current offset (cur_off).
 648                  */
 649                 data = hole = cur_off;
 650                 rc = smb_fsop_next_alloc_range(ofile->f_cr,
 651                     ofile->f_node, &data, &hole);
 652                 switch (rc) {
 653                 case 0:
 654                         /* Found (data, hole) */
 655                         break;
 656                 case ENXIO:
 657                         /*
 658                          * No more data after cur_off.
 659                          * Will encode one last hole.
 660                          */
 661                         data = hole = eof;
 662                         break;
 663                 default:
 664                         cmn_err(CE_NOTE, "smb_fsop_next_alloc_range: rc=%d",
 665                             rc);
 666                         /* FALLTHROUGH */
 667                 case ENOSYS:    /* FS does not support VOP_IOCTL... */
 668                 case ENOTTY:    /* ... or _FIO_SEEK_DATA, _HOLE */
 669                         data = cur_off;
 670                         hole = eof;
 671                         break;
 672                 }
 673                 DTRACE_PROBE2(range2, uint64_t, data, uint64_t, hole);
 674         }
 675 
 676         /*
 677          * Overwrite the fixed part of the response with the
 678          * final numbers of regions etc.
 679          * Flags, TotalRegionEntryCount, RegionEntryCount, Reserved
 680          */
 681         (void) smb_mbc_poke(fsctl->out_mbc, 0, "llll",
 682             0, // flags
 683             tot_regions,
 684             put_regions,
 685             0); // reserved
 686 
 687         if (put_regions < tot_regions)
 688                 return (NT_STATUS_BUFFER_OVERFLOW);
 689 
 690         return (NT_STATUS_SUCCESS);
 691 }