Print this page
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-3409 SMB2: OSX - cannot display nested folders in finder
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3733 Want SMB2 Apple extensions
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-2604 Windows Explorer Stops Working / Crashes when Security Tab is accessed
NEX-2593 SMB2: unable to create folder with long filename on 4.0.3
NEX-1409 bcmp(NULL, NULL, 0) panics in DEBUG kernel
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
SMB-143 OS X 10.9.2 not working with SMB2
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
SMB-105 Codenomicon: SMB2 TC: 384126: Panic in smb2_query_dir with null search pattern
SMB-110 panic mapping a share from Nexentastor to the windows 2012 R2 client
SMB-109 Codenomicon: SMB TC: 409480 - Panic with SMB2_FIND request
SMB-96 Codenomicon: SMB2 TC: 141500 - Panic in smb2_decode_create_ctx
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   *
  25      - * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
       25 + * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  26   26   */
  27   27  
  28   28  /*
  29   29   * Dispatch function for SMB2_QUERY_DIRECTORY
  30   30   *
  31   31   * Similar to smb_trans2_find.c (from SMB1)
  32   32   */
  33   33  
  34   34  #include <smbsrv/smb2_kproto.h>
       35 +#include <smbsrv/smb2_aapl.h>
  35   36  
  36   37  /*
       38 + * Internally defined info. level for MacOS support.
       39 + * Make sure this does not conflict with real values in
       40 + * FILE_INFORMATION_CLASS, and that it fits in 8-bits.
       41 + */
       42 +#define FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
       43 +
       44 +/*
  37   45   * Args (and other state) that we carry around among the
  38   46   * various functions involved in SMB2 Query Directory.
  39   47   */
  40   48  typedef struct smb2_find_args {
  41   49          uint32_t fa_maxdata;
  42   50          uint8_t fa_infoclass;
  43   51          uint8_t fa_fflags;
  44   52          uint16_t fa_maxcount;
  45   53          uint16_t fa_eos;        /* End Of Search */
  46   54          uint16_t fa_fixedsize;  /* size of fixed part of a returned entry */
  47   55          uint32_t fa_lastkey;    /* Last resume key */
  48   56          int fa_last_entry;      /* offset of last entry */
       57 +
       58 +        /* Normal info, per dir. entry */
       59 +        smb_fileinfo_t fa_fi;
       60 +
       61 +        /* MacOS AAPL extension stuff. */
       62 +        smb_macinfo_t fa_mi;
  49   63  } smb2_find_args_t;
  50   64  
  51   65  static uint32_t smb2_find_entries(smb_request_t *,
  52   66      smb_odir_t *, smb2_find_args_t *);
  53      -static uint32_t smb2_find_mbc_encode(smb_request_t *,
  54      -    smb_fileinfo_t *, smb2_find_args_t *);
       67 +static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);
  55   68  
  56   69  /*
  57   70   * Tunable parameter to limit the maximum
  58   71   * number of entries to be returned.
  59   72   */
  60   73  uint16_t smb2_find_max = 128;
  61   74  
  62   75  smb_sdrc_t
  63   76  smb2_query_dir(smb_request_t *sr)
  64   77  {
  65      -        smb2_find_args_t                args;
  66      -        smb_odir_resume_t       odir_resume;
       78 +        smb2_find_args_t args;
       79 +        smb_odir_resume_t odir_resume;
  67   80          smb_ofile_t *of = NULL;
  68   81          smb_odir_t *od = NULL;
  69   82          char *pattern = NULL;
  70   83          uint16_t StructSize;
  71   84          uint32_t FileIndex;
  72   85          uint16_t NameOffset;
  73   86          uint16_t NameLength;
  74   87          smb2fid_t smb2fid;
  75   88          uint16_t sattr = SMB_SEARCH_ATTRIBUTES;
  76   89          uint16_t DataOff;
↓ open down ↓ 15 lines elided ↑ open up ↑
  92  105              &FileIndex,                 /* l */
  93  106              &smb2fid.persistent,        /* q */
  94  107              &smb2fid.temporal,          /* q */
  95  108              &NameOffset,                /* w */
  96  109              &NameLength,                /* w */
  97  110              &args.fa_maxdata);          /* l */
  98  111          if (rc || StructSize != 33)
  99  112                  return (SDRC_ERROR);
 100  113  
 101  114          status = smb2sr_lookup_fid(sr, &smb2fid);
      115 +        of = sr->fid_ofile;
      116 +
      117 +        DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
      118 +
 102  119          if (status)
 103  120                  goto errout;
 104      -        of = sr->fid_ofile;
 105  121  
 106  122          /*
 107  123           * If there's an input buffer (search pattern), decode it.
 108  124           * Two times MAXNAMELEN because it represents the UNICODE string
 109  125           * length in bytes.
 110  126           */
 111  127          if (NameLength >= (2 * MAXNAMELEN)) {
 112  128                  status = NT_STATUS_OBJECT_PATH_INVALID;
 113  129                  goto errout;
 114  130          }
↓ open down ↓ 20 lines elided ↑ open up ↑
 135  151                  pattern = "*";
 136  152  
 137  153          /*
 138  154           * Setup the output buffer.
 139  155           */
 140  156          if (args.fa_maxdata > smb2_max_trans)
 141  157                  args.fa_maxdata = smb2_max_trans;
 142  158          sr->raw_data.max_bytes = args.fa_maxdata;
 143  159  
 144  160          /*
 145      -         * Get the mininum size of entries we will return, which
      161 +         * Get the fixed size of entries we will return, which
 146  162           * lets us estimate the number of entries we'll need.
 147      -         * This should be the size with a one character name.
 148      -         * Compare w/ smb2_find_get_maxdata().
 149  163           *
 150  164           * Also use this opportunity to validate fa_infoclass.
 151  165           */
 152  166  
 153  167          switch (args.fa_infoclass) {
 154  168          case FileDirectoryInformation:          /* 1 */
 155  169                  args.fa_fixedsize = 64;
 156  170                  break;
 157  171          case FileFullDirectoryInformation:      /* 2 */
 158  172                  args.fa_fixedsize = 68;
↓ open down ↓ 8 lines elided ↑ open up ↑
 167  181                  args.fa_fixedsize = 96;
 168  182                  break;
 169  183          case FileIdFullDirectoryInformation:    /* 38 */
 170  184                  args.fa_fixedsize = 84;
 171  185                  break;
 172  186          default:
 173  187                  status = NT_STATUS_INVALID_INFO_CLASS;
 174  188                  goto errout;
 175  189          }
 176  190  
      191 +        /*
      192 +         * MacOS, when using the AAPL CreateContext extensions
      193 +         * and the "read dir attr" feature, uses a non-standard
      194 +         * information format for directory entries.  Internally
      195 +         * we'll use a fake info level to represent this case.
      196 +         * (Wish they had just defined a new info level.)
      197 +         */
      198 +        if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
      199 +            args.fa_infoclass == FileIdBothDirectoryInformation) {
      200 +                args.fa_infoclass = FileIdMacOsDirectoryInformation;
      201 +                args.fa_fixedsize = 96; /* yes, same size */
      202 +        }
      203 +
 177  204          args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
 178  205          if (args.fa_maxcount == 0)
 179  206                  args.fa_maxcount = 1;
 180  207          if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
 181  208                  args.fa_maxcount = smb2_find_max;
 182  209          if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
 183  210                  args.fa_maxcount = 1;
 184  211  
 185  212          /*
 186  213           * If this ofile does not have an odir yet, get one.
↓ open down ↓ 32 lines elided ↑ open up ↑
 219  246          smb_odir_resume_at(od, &odir_resume);
 220  247          of->f_seek_pos = od->d_offset;
 221  248  
 222  249          /*
 223  250           * The real work of readdir and format conversion.
 224  251           */
 225  252          status = smb2_find_entries(sr, od, &args);
 226  253  
 227  254          of->f_seek_pos = od->d_offset;
 228  255  
 229      -        if (status == NT_STATUS_NO_MORE_FILES) {
 230      -                if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) {
 231      -                        status = NT_STATUS_NO_SUCH_FILE;
 232      -                        goto errout;
 233      -                }
 234      -                /*
 235      -                 * This is not an error, but a warning that can be
 236      -                 * used to tell the client that this data return
 237      -                 * is the last of the enumeration.  Returning this
 238      -                 * warning now (with the data) saves the client a
 239      -                 * round trip that would otherwise be needed to
 240      -                 * find out it's at the end.
 241      -                 */
 242      -                sr->smb2_status = status;
 243      -                status = 0;
      256 +        if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
      257 +            status == NT_STATUS_NO_MORE_FILES) {
      258 +                status = NT_STATUS_NO_SUCH_FILE;
 244  259          }
 245      -        if (status)
 246      -                goto errout;
 247  260  
      261 +errout:
      262 +        sr->smb2_status = status;
      263 +        DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
      264 +
 248  265          /*
      266 +         * Note: NT_STATUS_NO_MORE_FILES is a warning
      267 +         * used to tell the client that this data return
      268 +         * is the last of the enumeration.  Returning this
      269 +         * warning now (with the data) saves the client a
      270 +         * round trip that would otherwise be needed to
      271 +         * find out it's at the end.
      272 +         */
      273 +        if (status != 0 &&
      274 +            status != NT_STATUS_NO_MORE_FILES) {
      275 +                smb2sr_put_error(sr, status);
      276 +                return (SDRC_SUCCESS);
      277 +        }
      278 +
      279 +        /*
 249  280           * SMB2 Query Directory reply
 250  281           */
 251  282          StructSize = 9;
 252  283          DataOff = SMB2_HDR_SIZE + 8;
 253  284          DataLen = MBC_LENGTH(&sr->raw_data);
 254  285          rc = smb_mbc_encodef(
 255  286              &sr->reply, "wwlC",
 256  287              StructSize,         /* w */
 257  288              DataOff,            /* w */
 258  289              DataLen,            /* l */
 259  290              &sr->raw_data);     /* C */
 260  291          if (DataLen == 0)
 261  292                  (void) smb_mbc_encodef(&sr->reply, ".");
 262      -        if (rc == 0)
 263      -                return (SDRC_SUCCESS);
 264      -        status = NT_STATUS_UNSUCCESSFUL;
 265  293  
 266      -errout:
 267      -        smb2sr_put_error(sr, status);
      294 +        if (rc)
      295 +                sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
      296 +
 268  297          return (SDRC_SUCCESS);
 269  298  }
 270  299  
 271  300  /*
 272  301   * smb2_find_entries
 273  302   *
 274  303   * Find and encode up to args->fa_maxcount directory entries.
 275  304   *
 276  305   * Returns:
 277  306   *   NT status
 278  307   */
 279  308  static uint32_t
 280  309  smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
 281  310  {
 282      -        smb_fileinfo_t  fileinfo;
 283  311          smb_odir_resume_t odir_resume;
      312 +        char            *tbuf = NULL;
      313 +        size_t          tbuflen = 0;
 284  314          uint16_t        count;
 285  315          uint16_t        minsize;
 286  316          uint32_t        status = 0;
 287  317          int             rc = -1;
 288  318  
 289  319          /*
 290  320           * Let's stop when the remaining space will not hold a
 291  321           * minimum size entry.  That's the fixed part plus the
 292  322           * storage size of a 1 char unicode string.
 293  323           */
 294  324          minsize = args->fa_fixedsize + 2;
 295  325  
      326 +        /*
      327 +         * FileIdMacOsDirectoryInformation needs some buffer space
      328 +         * for composing directory entry + stream name for lookup.
      329 +         * Get the buffer now to avoid alloc/free per entry.
      330 +         */
      331 +        if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
      332 +                tbuflen = 2 * MAXNAMELEN;
      333 +                tbuf = kmem_alloc(tbuflen, KM_SLEEP);
      334 +        }
      335 +
 296  336          count = 0;
 297  337          while (count < args->fa_maxcount) {
 298  338  
 299  339                  if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
 300  340                          status = NT_STATUS_BUFFER_OVERFLOW;
 301  341                          break;
 302  342                  }
 303  343  
 304      -                rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
      344 +                rc = smb_odir_read_fileinfo(sr, od,
      345 +                    &args->fa_fi, &args->fa_eos);
 305  346                  if (rc == ENOENT) {
 306  347                          status = NT_STATUS_NO_MORE_FILES;
 307  348                          break;
 308  349                  }
 309  350                  if (rc != 0) {
 310  351                          status = smb_errno2status(rc);
 311  352                          break;
 312  353                  }
 313  354                  if (args->fa_eos != 0) {
 314  355                          /* The readdir call hit the end. */
 315  356                          status = NT_STATUS_NO_MORE_FILES;
 316  357                          break;
 317  358                  }
 318  359  
 319      -                status = smb2_find_mbc_encode(sr, &fileinfo, args);
      360 +                if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
      361 +                        (void) smb2_aapl_get_macinfo(sr, od,
      362 +                            &args->fa_fi, &args->fa_mi, tbuf, tbuflen);
      363 +
      364 +                if (smb2_aapl_use_file_ids == 0 &&
      365 +                    (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
      366 +                        args->fa_fi.fi_nodeid = 0;
      367 +
      368 +                status = smb2_find_mbc_encode(sr, args);
 320  369                  if (status) {
 321  370                          /*
 322  371                           * We read a directory entry but failed to
 323  372                           * copy it into the output buffer.  Rewind
 324  373                           * the directory pointer so this will be
 325  374                           * the first entry read next time.
 326  375                           */
 327  376                          bzero(&odir_resume, sizeof (odir_resume));
 328  377                          odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 329  378                          odir_resume.or_cookie = args->fa_lastkey;
 330  379                          smb_odir_resume_at(od, &odir_resume);
 331  380                          break;
 332  381                  }
 333  382  
 334  383                  /*
 335  384                   * Save the offset of the next entry we'll read.
 336  385                   * If we fail copying, we'll need this offset.
 337  386                   */
 338      -                args->fa_lastkey = fileinfo.fi_cookie;
      387 +                args->fa_lastkey = args->fa_fi.fi_cookie;
 339  388                  ++count;
 340  389          }
 341  390  
 342  391          if (count == 0) {
 343  392                  ASSERT(status != 0);
 344  393          } else {
 345  394                  /*
 346  395                   * We copied some directory entries, but stopped for
 347  396                   * NT_STATUS_NO_MORE_FILES, or something.
 348  397                   *
 349  398                   * Per [MS-FSCC] sec. 2.4, the last entry in the
 350  399                   * enumeration MUST have its NextEntryOffset value
 351  400                   * set to zero.  Overwrite that in the last entry.
 352  401                   */
 353  402                  (void) smb_mbc_poke(&sr->raw_data,
 354  403                      args->fa_last_entry, "l", 0);
 355  404                  status = 0;
 356  405          }
 357  406  
      407 +        if (tbuf != NULL)
      408 +                kmem_free(tbuf, tbuflen);
      409 +
 358  410          return (status);
 359  411  }
 360  412  
 361  413  /*
 362  414   * smb2_mbc_encode
 363  415   *
 364  416   * This function encodes the mbc for one directory entry.
 365  417   *
 366  418   * The function returns -1 when the max data requested by client
 367  419   * is reached. If the entry is valid and successful encoded, 0
↓ open down ↓ 4 lines elided ↑ open up ↑
 372  424   * in the next_entry_offset. namelen is the unterminated length of
 373  425   * the filename. For levels except STANDARD and EA_SIZE, if the
 374  426   * filename is ascii the name length returned to the client should
 375  427   * include the null terminator. Otherwise the length returned to
 376  428   * the client should not include the terminator.
 377  429   *
 378  430   * Returns: 0 - data successfully encoded
 379  431   *      NT status
 380  432   */
 381  433  static uint32_t
 382      -smb2_find_mbc_encode(smb_request_t *sr, smb_fileinfo_t *fileinfo,
 383      -        smb2_find_args_t *args)
      434 +smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)
 384  435  {
      436 +        smb_fileinfo_t  *fileinfo = &args->fa_fi;
      437 +        smb_macinfo_t   *macinfo = &args->fa_mi;
 385  438          uint8_t         buf83[26];
 386  439          smb_msgbuf_t    mb;
 387      -        int             namelen, padsz;
      440 +        int             namelen;
 388  441          int             shortlen = 0;
 389  442          int             rc, starting_offset;
 390  443          uint32_t        next_entry_offset;
 391  444          uint32_t        mb_flags = SMB_MSGBUF_UNICODE;
 392  445          uint32_t        resume_key;
 393  446  
 394  447          namelen = smb_wcequiv_strlen(fileinfo->fi_name);
 395  448          if (namelen == -1)
 396  449                  return (NT_STATUS_INTERNAL_ERROR);
 397  450  
↓ open down ↓ 119 lines elided ↑ open up ↑
 517  570                      namelen,                    /* l */
 518  571                      0L,         /* EaSize          l */
 519  572                      shortlen,                   /* b. */
 520  573                      buf83,                      /* 24c */
 521  574                      /* reserved                    .. */
 522  575                      fileinfo->fi_nodeid);       /* q */
 523  576  
 524  577                  smb_msgbuf_term(&mb);
 525  578                  break;
 526  579  
      580 +        /*
      581 +         * MacOS, when using the AAPL extensions (see smb2_create)
      582 +         * uses modified directory listing responses where the
      583 +         * "EA size" field is replaced with "maximum access".
      584 +         * This avoids the need for MacOS Finder to come back
      585 +         * N times to get the maximum access for every file.
      586 +         */
      587 +        case FileIdMacOsDirectoryInformation:
      588 +                rc = smb_mbc_encodef(
      589 +                    &sr->raw_data, "llTTTTqqll",
      590 +                    0,  /* NextEntryOffset (set later) */
      591 +                    resume_key,         /* a.k.a. file index */
      592 +                    &fileinfo->fi_crtime,
      593 +                    &fileinfo->fi_atime,
      594 +                    &fileinfo->fi_mtime,
      595 +                    &fileinfo->fi_ctime,
      596 +                    fileinfo->fi_size,          /* q */
      597 +                    fileinfo->fi_alloc_size,    /* q */
      598 +                    fileinfo->fi_dosattr,       /* l */
      599 +                    namelen);                   /* l */
      600 +                if (rc != 0)
      601 +                        break;
      602 +                /*
      603 +                 * This where FileIdMacOsDirectoryInformation
      604 +                 * differs from FileIdBothDirectoryInformation
      605 +                 * Instead of: EaSize, ShortNameLen, ShortName;
      606 +                 * MacOS wants: MaxAccess, ResourceForkSize, and
      607 +                 * 16 bytes of "compressed finder info".
      608 +                 * mi_rforksize + mi_finderinfo falls where
      609 +                 * the 24 byte shortname would normally be.
      610 +                 */
      611 +                rc = smb_mbc_encodef(
      612 +                    &sr->raw_data, "l..q16cwq",
      613 +                    macinfo->mi_maxaccess,      /* l */
      614 +                    /* short_name_len, reserved  (..) */
      615 +                    macinfo->mi_rforksize,      /* q */
      616 +                    macinfo->mi_finderinfo,     /* 16c */
      617 +                    macinfo->mi_unixmode,       /* w */
      618 +                    fileinfo->fi_nodeid);       /* q */
      619 +                break;
      620 +
 527  621          /* See also: SMB_FIND_FILE_NAMES_INFO */
 528  622          case FileNamesInformation:              /* 12 */
 529  623                  rc = smb_mbc_encodef(
 530  624                      &sr->raw_data, "lll",
 531  625                      0,  /* NextEntryOffset (set later) */
 532  626                      resume_key,
 533  627                      namelen);
 534  628                  break;
 535  629  
 536  630          default:
↓ open down ↓ 9 lines elided ↑ open up ↑
 546  640           * Also store this offset for the caller so they can
 547  641           * patch this (again) to zero on the very last entry.
 548  642           */
 549  643          rc = smb_mbc_encodef(
 550  644              &sr->raw_data, "U",
 551  645              fileinfo->fi_name);
 552  646          if (rc)
 553  647                  return (NT_STATUS_BUFFER_OVERFLOW);
 554  648  
 555  649          /* Next entry needs to be 8-byte aligned. */
 556      -        padsz = sr->raw_data.chain_offset & 7;
 557      -        if (padsz) {
 558      -                padsz = 8 - padsz;
 559      -                (void) smb_mbc_encodef(&sr->raw_data, "#.", padsz);
 560      -        }
      650 +        (void) smb_mbc_put_align(&sr->raw_data, 8);
      651 +
 561  652          next_entry_offset = sr->raw_data.chain_offset - starting_offset;
 562  653          (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
 563  654              next_entry_offset);
 564  655          args->fa_last_entry = starting_offset;
 565  656  
 566  657          return (0);
 567  658  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX