Print this page
    
11083 support NFS server in zone
Portions contributed by: Dan Kruchinin <dan.kruchinin@nexenta.com>
Portions contributed by: Stepan Zastupov <stepan.zastupov@gmail.com>
Portions contributed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Portions contributed by: Mike Zeller <mike@mikezeller.net>
Portions contributed by: Dan McDonald <danmcd@joyent.com>
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Portions contributed by: Vitaliy Gusev <gusev.vitaliy@gmail.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Jason King <jbk@joyent.com>
Reviewed by: C Fraire <cfraire@me.com>
Change-Id: I22f289d357503f9b48a0bc2482cc4328a6d43d16
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/nfs/nfs_cmd.c
          +++ new/usr/src/uts/common/fs/nfs/nfs_cmd.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  
    | 
      ↓ open down ↓ | 
    10 lines elided | 
    
      ↑ open up ↑ | 
  
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  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   23   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23   24   * Use is subject to license terms.
  24   25   */
  25   26  
       27 +/*
       28 + * Copyright 2018 Nexenta Systems, Inc.
       29 + */
       30 +
  26   31  #include <sys/param.h>
  27   32  #include <sys/types.h>
  28   33  #include <sys/pathname.h>
  29   34  #include <sys/errno.h>
  30   35  #include <sys/cmn_err.h>
  31   36  #include <sys/debug.h>
  32   37  #include <sys/systm.h>
  33   38  #include <sys/unistd.h>
  34   39  #include <sys/door.h>
  35   40  #include <sys/socket.h>
  36   41  #include <nfs/export.h>
  37   42  #include <nfs/nfs_cmd.h>
  
    | 
      ↓ open down ↓ | 
    2 lines elided | 
    
      ↑ open up ↑ | 
  
  38   43  #include <sys/kmem.h>
  39   44  #include <sys/sunddi.h>
  40   45  
  41   46  #define NFSCMD_DR_TRYCNT        8
  42   47  
  43   48  #ifdef nextdp
  44   49  #undef nextdp
  45   50  #endif
  46   51  #define nextdp(dp)      ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
  47   52  
  48      -kmutex_t        nfscmd_lock;
  49      -door_handle_t   nfscmd_dh;
       53 +typedef struct nfscmd_globals {
       54 +        kmutex_t        nfscmd_lock;
       55 +        door_handle_t   nfscmd_dh;
       56 +} nfscmd_globals_t;
  50   57  
       58 +static zone_key_t nfscmd_zone_key;
       59 +
  51   60  static struct charset_cache *nfscmd_charmap(exportinfo_t *exi,
  52   61      struct sockaddr *sp);
       62 +static void *nfscmd_zone_init(zoneid_t);
       63 +static void nfscmd_zone_fini(zoneid_t, void *);
  53   64  
  54      -
  55   65  void
  56   66  nfscmd_args(uint_t did)
  57   67  {
  58      -        mutex_enter(&nfscmd_lock);
  59      -        if (nfscmd_dh)
  60      -                door_ki_rele(nfscmd_dh);
  61      -        nfscmd_dh = door_ki_lookup(did);
  62      -        mutex_exit(&nfscmd_lock);
       68 +        nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
       69 +
       70 +        mutex_enter(&ncg->nfscmd_lock);
       71 +        if (ncg->nfscmd_dh != NULL)
       72 +                door_ki_rele(ncg->nfscmd_dh);
       73 +        ncg->nfscmd_dh = door_ki_lookup(did);
       74 +        mutex_exit(&ncg->nfscmd_lock);
  63   75  }
  64   76  
  65   77  void
  66   78  nfscmd_init(void)
  67   79  {
  68      -        mutex_init(&nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
       80 +        zone_key_create(&nfscmd_zone_key, nfscmd_zone_init,
       81 +            NULL, nfscmd_zone_fini);
  69   82  }
  70   83  
  71   84  void
  72   85  nfscmd_fini(void)
  73   86  {
       87 +        (void) zone_key_delete(nfscmd_zone_key);
  74   88  }
  75   89  
       90 +/*ARGSUSED*/
       91 +static void *
       92 +nfscmd_zone_init(zoneid_t zoneid)
       93 +{
       94 +        nfscmd_globals_t *ncg;
       95 +
       96 +        ncg = kmem_zalloc(sizeof (*ncg), KM_SLEEP);
       97 +        mutex_init(&ncg->nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
       98 +
       99 +        return (ncg);
      100 +}
      101 +
      102 +/*ARGSUSED*/
      103 +static void
      104 +nfscmd_zone_fini(zoneid_t zoneid, void *data)
      105 +{
      106 +        nfscmd_globals_t *ncg = data;
      107 +
      108 +        mutex_destroy(&ncg->nfscmd_lock);
      109 +        if (ncg->nfscmd_dh)
      110 +                door_ki_rele(ncg->nfscmd_dh);
      111 +        kmem_free(ncg, sizeof (*ncg));
      112 +}
      113 +
  76  114  /*
  77  115   * nfscmd_send(arg, result)
  78  116   *
  79  117   * Send a command to the daemon listening on the door. The result is
  80  118   * returned in the result pointer if the function return value is
  81  119   * NFSCMD_ERR_SUCCESS. Otherwise it is the error value.
  82  120   */
  83  121  int
  84  122  nfscmd_send(nfscmd_arg_t *arg, nfscmd_res_t *res)
  85  123  {
  86  124          door_handle_t   dh;
  87  125          door_arg_t      da;
  88  126          door_info_t     di;
  89  127          int             ntries = 0;
  90  128          int             last = 0;
      129 +        nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
  91  130  
  92  131  retry:
  93      -        mutex_enter(&nfscmd_lock);
  94      -        dh = nfscmd_dh;
      132 +        mutex_enter(&ncg->nfscmd_lock);
      133 +        dh = ncg->nfscmd_dh;
  95  134          if (dh != NULL)
  96  135                  door_ki_hold(dh);
  97      -        mutex_exit(&nfscmd_lock);
      136 +        mutex_exit(&ncg->nfscmd_lock);
  98  137  
  99  138          if (dh == NULL) {
 100  139                  /*
 101  140                   * The rendezvous point has not been established yet !
 102  141                   * This could mean that either mountd(1m) has not yet
 103  142                   * been started or that _this_ routine nuked the door
 104  143                   * handle after receiving an EINTR for a REVOKED door.
 105  144                   *
 106  145                   * Returning NFSAUTH_DROP will cause the NFS client
 107  146                   * to retransmit the request, so let's try to be more
 108  147                   * rescillient and attempt for ntries before we bail.
 109  148                   */
 110  149                  if (++ntries % NFSCMD_DR_TRYCNT) {
 111  150                          delay(hz);
 112  151                          goto retry;
 113  152                  }
 114  153                  return (NFSCMD_ERR_DROP);
 115  154          }
 116  155  
 117  156          da.data_ptr = (char *)arg;
 118  157          da.data_size = sizeof (nfscmd_arg_t);
 119  158          da.desc_ptr = NULL;
 120  159          da.desc_num = 0;
 121  160          da.rbuf = (char *)res;
 122  161          da.rsize = sizeof (nfscmd_res_t);
 123  162  
 124  163          switch (door_ki_upcall(dh, &da)) {
 125  164          case 0:
 126  165                  /* Success */
 127  166                  break;
 128  167          case EAGAIN:
 129  168                  /* Need to retry a couple of times */
 130  169                  door_ki_rele(dh);
 131  170                  delay(hz);
 132  171                  goto retry;
 133  172                  /* NOTREACHED */
  
    | 
      ↓ open down ↓ | 
    26 lines elided | 
    
      ↑ open up ↑ | 
  
 134  173          case EINTR:
 135  174                  if (!door_ki_info(dh, &di)) {
 136  175                          if (di.di_attributes & DOOR_REVOKED) {
 137  176                                  /*
 138  177                                   * The server barfed and revoked
 139  178                                   * the (existing) door on us; we
 140  179                                   * want to wait to give smf(5) a
 141  180                                   * chance to restart mountd(1m)
 142  181                                   * and establish a new door handle.
 143  182                                   */
 144      -                                mutex_enter(&nfscmd_lock);
 145      -                                if (dh == nfscmd_dh)
 146      -                                        nfscmd_dh = NULL;
 147      -                                mutex_exit(&nfscmd_lock);
      183 +                                mutex_enter(&ncg->nfscmd_lock);
      184 +                                if (dh == ncg->nfscmd_dh)
      185 +                                        ncg->nfscmd_dh = NULL;
      186 +                                mutex_exit(&ncg->nfscmd_lock);
 148  187                                  door_ki_rele(dh);
 149  188                                  delay(hz);
 150  189                                  goto retry;
 151  190                          }
 152  191                          /*
 153  192                           * If the door was _not_ revoked on us,
 154  193                           * then more than likely we took an INTR,
 155  194                           * so we need to fail the operation.
 156  195                           */
 157  196                          door_ki_rele(dh);
 158  197                  }
 159  198                  /*
 160  199                   * The only failure that can occur from getting
 161  200                   * the door info is EINVAL, so we let the code
 162  201                   * below handle it.
 163  202                   */
 164  203                  /* FALLTHROUGH */
 165  204  
 166  205          case EBADF:
 167  206          case EINVAL:
 168  207          default:
 169  208                  /*
 170  209                   * If we have a stale door handle, give smf a last
 171  210                   * chance to start it by sleeping for a little bit.
 172  211                   * If we're still hosed, we'll fail the call.
 173  212                   *
 174  213                   * Since we're going to reacquire the door handle
 175  214                   * upon the retry, we opt to sleep for a bit and
 176  215                   * _not_ to clear mountd_dh. If mountd restarted
 177  216                   * and was able to set mountd_dh, we should see
 178  217                   * the new instance; if not, we won't get caught
 179  218                   * up in the retry/DELAY loop.
 180  219                   */
 181  220                  door_ki_rele(dh);
 182  221                  if (!last) {
 183  222                          delay(hz);
 184  223                          last++;
 185  224                          goto retry;
 186  225                  }
 187  226                  res->error = NFSCMD_ERR_FAIL;
 188  227                  break;
 189  228          }
 190  229          return (res->error);
 191  230  }
 192  231  
 193  232  /*
 194  233   * nfscmd_findmap(export, addr)
 195  234   *
 196  235   * Find a characterset map for the specified client address.
 197  236   * First try to find a cached entry. If not successful,
 198  237   * ask mountd daemon running in userland.
 199  238   *
 200  239   * For most of the clients this function is NOOP, since
 201  240   * EX_CHARMAP flag won't be set.
 202  241   */
 203  242  struct charset_cache *
 204  243  nfscmd_findmap(struct exportinfo *exi, struct sockaddr *sp)
 205  244  {
 206  245          struct charset_cache *charset;
 207  246  
 208  247          /*
 209  248           * In debug kernel we want to know about strayed nulls.
 210  249           * In non-debug kernel we behave gracefully.
 211  250           */
 212  251          ASSERT(exi != NULL);
 213  252          ASSERT(sp != NULL);
 214  253  
 215  254          if (exi == NULL || sp == NULL)
 216  255                  return (NULL);
 217  256  
 218  257          mutex_enter(&exi->exi_lock);
 219  258  
 220  259          if (!(exi->exi_export.ex_flags & EX_CHARMAP)) {
 221  260                  mutex_exit(&exi->exi_lock);
 222  261                  return (NULL);
 223  262          }
 224  263  
 225  264          for (charset = exi->exi_charset;
 226  265              charset != NULL;
 227  266              charset = charset->next) {
 228  267                  if (bcmp(sp, &charset->client_addr,
 229  268                      sizeof (struct sockaddr)) == 0)
 230  269                          break;
 231  270          }
 232  271          mutex_exit(&exi->exi_lock);
 233  272  
 234  273          /* the slooow way - ask daemon */
 235  274          if (charset == NULL)
 236  275                  charset = nfscmd_charmap(exi, sp);
 237  276  
 238  277          return (charset);
 239  278  }
 240  279  
 241  280  /*
 242  281   * nfscmd_insert_charmap(export, addr, name)
 243  282   *
 244  283   * Insert a new character set conversion map into the export structure
 245  284   * for the share. The entry has the IP address of the client and the
 246  285   * character set name.
 247  286   */
 248  287  
 249  288  static struct charset_cache *
 250  289  nfscmd_insert_charmap(struct exportinfo *exi, struct sockaddr *sp, char *name)
 251  290  {
 252  291          struct charset_cache *charset;
 253  292  
 254  293          charset = (struct charset_cache *)
 255  294              kmem_zalloc(sizeof (struct charset_cache), KM_SLEEP);
 256  295  
 257  296          if (charset == NULL)
 258  297                  return (NULL);
 259  298          if (name != NULL) {
 260  299                  charset->inbound = kiconv_open("UTF-8", name);
 261  300                  charset->outbound = kiconv_open(name, "UTF-8");
 262  301          }
 263  302          charset->client_addr = *sp;
 264  303          mutex_enter(&exi->exi_lock);
 265  304          charset->next = exi->exi_charset;
 266  305          exi->exi_charset = charset;
 267  306          mutex_exit(&exi->exi_lock);
 268  307  
 269  308          return (charset);
 270  309  }
 271  310  
 272  311  /*
 273  312   * nfscmd_charmap(response, sp, exi)
 274  313   *
 275  314   * Check to see if this client needs a character set conversion.
 276  315   */
 277  316  static struct charset_cache *
 278  317  nfscmd_charmap(exportinfo_t *exi, struct sockaddr *sp)
 279  318  {
 280  319          nfscmd_arg_t req;
 281  320          int ret;
 282  321          char *path;
 283  322          nfscmd_res_t res;
 284  323          struct charset_cache *charset;
 285  324  
 286  325          path = exi->exi_export.ex_path;
 287  326          if (path == NULL)
 288  327                  return (NULL);
 289  328  
 290  329          /*
 291  330           * nfscmd_findmap() did not find one in the cache so make
 292  331           * the request to the daemon. We need to add the entry in
 293  332           * either case since we want negative as well as
 294  333           * positive cacheing.
 295  334           */
 296  335          req.cmd = NFSCMD_CHARMAP_LOOKUP;
 297  336          req.version = NFSCMD_VERSION;
 298  337          req.arg.charmap.addr = *sp;
 299  338          (void) strncpy(req.arg.charmap.path, path, MAXPATHLEN);
 300  339          bzero((caddr_t)&res, sizeof (nfscmd_res_t));
 301  340          ret = nfscmd_send(&req, &res);
 302  341          if (ret == NFSCMD_ERR_SUCCESS)
 303  342                  charset = nfscmd_insert_charmap(exi, sp,
 304  343                      res.result.charmap.codeset);
 305  344          else
 306  345                  charset = nfscmd_insert_charmap(exi, sp, NULL);
 307  346  
 308  347          return (charset);
 309  348  }
 310  349  
 311  350  /*
 312  351   * nfscmd_convname(addr, export, name, inbound, size)
 313  352   *
 314  353   * Convert the given "name" string to the appropriate character set.
 315  354   * If inbound is true, convert from the client character set to UTF-8.
 316  355   * If inbound is false, convert from UTF-8 to the client characters set.
 317  356   *
 318  357   * In case of NFS v4 this is used for ill behaved clients, since
 319  358   * according to the standard all file names should be utf-8 encoded
 320  359   * on client-side.
 321  360   */
 322  361  
 323  362  char *
 324  363  nfscmd_convname(struct sockaddr *ca, struct exportinfo *exi, char *name,
 325  364      int inbound, size_t size)
 326  365  {
 327  366          char *newname;
 328  367          char *holdname;
 329  368          int err;
 330  369          int ret;
 331  370          size_t nsize;
 332  371          size_t osize;
 333  372          struct charset_cache *charset = NULL;
 334  373  
 335  374          charset = nfscmd_findmap(exi, ca);
 336  375          if (charset == NULL ||
 337  376              (charset->inbound == NULL && inbound) ||
 338  377              (charset->outbound == NULL && !inbound))
 339  378                  return (name);
 340  379  
 341  380          /* make sure we have more than enough space */
 342  381          newname = kmem_zalloc(size, KM_SLEEP);
 343  382          nsize = strlen(name);
 344  383          osize = size;
 345  384          holdname = newname;
 346  385          if (inbound)
 347  386                  ret = kiconv(charset->inbound, &name, &nsize,
 348  387                      &holdname, &osize, &err);
 349  388          else
 350  389                  ret = kiconv(charset->outbound, &name, &nsize,
 351  390                      &holdname, &osize, &err);
 352  391          if (ret == (size_t)-1) {
 353  392                  kmem_free(newname, size);
 354  393                  newname = NULL;
 355  394          }
 356  395  
 357  396          return (newname);
 358  397  }
 359  398  
 360  399  /*
 361  400   * nfscmd_convdirent()
 362  401   *
 363  402   * There is only one entry in the data.  Convert to new charset, if
 364  403   * required and only return a success if it fits.
 365  404   */
 366  405  char *
 367  406  nfscmd_convdirent(struct sockaddr *ca, struct exportinfo *exi, char *data,
 368  407      size_t size, enum nfsstat3 *error)
 369  408  {
 370  409          char *newdata;
 371  410          size_t ret;
 372  411          size_t nsize;
 373  412          size_t count;
 374  413          int err = 0;
 375  414          char *iname;
 376  415          char *oname;
 377  416          struct charset_cache *charset;
 378  417  
 379  418          charset = nfscmd_findmap(exi, ca);
 380  419          if (charset == NULL || charset->outbound == (void *)~0)
 381  420                  return (data);
 382  421  
 383  422          newdata = kmem_zalloc(size, KM_SLEEP);
 384  423  
 385  424          nsize = strlen(((struct dirent64 *)data)->d_name);
 386  425          count = size;
 387  426          bcopy(data, newdata, sizeof (struct dirent64));
 388  427  
 389  428          iname = ((struct dirent64 *)data)->d_name;
 390  429          oname = ((struct dirent64 *)newdata)->d_name;
 391  430  
 392  431          ret = kiconv(charset->outbound, &iname, &nsize, &oname, &count, &err);
 393  432          if (ret == (size_t)-1) {
 394  433                  kmem_free(newdata, size);
 395  434                  newdata = NULL;
 396  435                  if (err == E2BIG) {
 397  436                          if (error != NULL)
 398  437                                  *error = NFS3ERR_NAMETOOLONG;
 399  438                  } else {
 400  439                          newdata = data;
 401  440                  }
 402  441          } else {
 403  442                  ret = strlen(((struct dirent64 *)newdata)->d_name);
 404  443                  ((struct dirent64 *)newdata)->d_reclen =
 405  444                      DIRENT64_RECLEN(ret + 1);
 406  445          }
 407  446          return (newdata);
 408  447  }
 409  448  
 410  449  /*
 411  450   * nfscmd_convdirplus(addr, export, data, nents, maxsize, ndata)
 412  451   *
 413  452   * Convert the dirents in data into a new list of dirents in ndata.
 414  453   */
 415  454  
 416  455  size_t
 417  456  nfscmd_convdirplus(struct sockaddr *ca, struct exportinfo *exi, char *data,
 418  457      size_t nents, size_t maxsize, char **ndata)
 419  458  {
 420  459          char *newdata;
 421  460          size_t nsize;
 422  461          struct dirent64 *dp;
 423  462          struct dirent64 *ndp;
 424  463          size_t i;
 425  464          size_t ret;
 426  465          char *iname;
 427  466          char *oname;
 428  467          size_t ilen;
 429  468          size_t olen;
 430  469          int err;
 431  470          size_t skipped;
 432  471          struct charset_cache *charset;
 433  472          *ndata = data;  /* return the data if no changes to make */
 434  473  
 435  474          charset = nfscmd_findmap(exi, ca);
 436  475  
 437  476          if (charset == NULL || charset->outbound == (void *)~0)
 438  477                  return (0);
 439  478  
 440  479          newdata = kmem_zalloc(maxsize, KM_SLEEP);
 441  480          nsize = 0;
 442  481  
 443  482          dp = (struct dirent64 *)data;
 444  483          ndp = (struct dirent64 *)newdata;
 445  484  
 446  485          for (skipped = 0, i = 0; i < nents; i++) {
 447  486                  /*
 448  487                   * Copy the dp information if it fits. Then copy and
 449  488                   * convert the name in the entry.
 450  489                   */
 451  490                  if ((maxsize - nsize) < dp->d_reclen)
 452  491                          /* doesn't fit */
 453  492                          break;
 454  493                  *ndp = *dp;
 455  494                  iname = dp->d_name;
 456  495                  ilen = strlen(iname);
 457  496                  oname = ndp->d_name;
 458  497                  olen = MIN(MAXNAMELEN, maxsize - nsize);
 459  498                  ret = kiconv(charset->outbound, &iname, &ilen, &oname,
 460  499                      &olen, &err);
 461  500  
 462  501                  if (ret == (size_t)-1) {
 463  502                          switch (err) {
 464  503                          default:
 465  504                          case E2BIG:
 466  505                                  break;
 467  506                          case EILSEQ:
 468  507                                  skipped++;
 469  508                                  dp = nextdp(dp);
 470  509                                  continue;
 471  510                          }
 472  511                  }
 473  512                  ilen = MIN(MAXNAMELEN, maxsize - nsize) - olen;
 474  513                  ndp->d_name[ilen] = '\0';
 475  514                  /*
 476  515                   * What to do with other errors?
 477  516                   * For now, we return the unconverted string.
 478  517                   */
 479  518                  ndp->d_reclen = DIRENT64_RECLEN(strlen(ndp->d_name) + 1);
 480  519                  nsize += ndp->d_reclen;
 481  520                  dp = nextdp(dp);
 482  521                  ndp = nextdp(ndp);
 483  522          }
 484  523  
 485  524          *ndata = newdata;
 486  525          return (nents - (i + skipped));
 487  526  }
 488  527  
 489  528  /*
 490  529   * nfscmd_countents(data, len)
 491  530   *
 492  531   * How many dirents are there in the data buffer?
 493  532   */
 494  533  
 495  534  size_t
 496  535  nfscmd_countents(char *data, size_t len)
 497  536  {
 498  537          struct dirent64 *dp = (struct dirent64 *)data;
 499  538          size_t curlen;
 500  539          size_t reclen;
 501  540          size_t nents;
 502  541  
 503  542          for (nents = 0, curlen = 0; curlen < len; curlen += reclen, nents++) {
 504  543                  reclen = dp->d_reclen;
 505  544                  dp = nextdp(dp);
 506  545          }
 507  546          return (nents);
 508  547  }
 509  548  
 510  549  /*
 511  550   * nfscmd_dropped_entrysize(dir, drop, nents)
 512  551   *
 513  552   * We need to drop "drop" entries from dir in order to fit in the
 514  553   * buffer.  How much do we reduce the overall size by?
 515  554   */
 516  555  
 517  556  size_t
 518  557  nfscmd_dropped_entrysize(struct dirent64 *dir, size_t drop, size_t nents)
 519  558  {
 520  559          size_t size;
 521  560          size_t i;
 522  561  
 523  562          for (i = nents - drop; i > 0 && dir != NULL; i--)
 524  563                  dir = nextdp(dir);
 525  564  
 526  565          if (dir == NULL)
 527  566                  return (0);
 528  567  
 529  568          for (size = 0, i = 0; i < drop && dir != NULL; i++) {
 530  569                  size += dir->d_reclen;
 531  570                  dir = nextdp(dir);
 532  571          }
 533  572          return (size);
 534  573  }
  
    | 
      ↓ open down ↓ | 
    377 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX