1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 /*
  27  *
  28  *                      res.c
  29  *
  30  * Implements routines to create a cache resource file.
  31  */
  32 
  33 #pragma ident   "%Z%%M% %I%     %E% SMI"
  34 
  35 #include <assert.h>
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <errno.h>
  39 #include <sys/stat.h>
  40 #include <sys/param.h>
  41 #include <sys/fcntl.h>
  42 #include <sys/mman.h>
  43 #include <sys/fs/cachefs_fs.h>
  44 #include "res.h"
  45 
  46 struct res {
  47         int                      p_magic;       /* magic number */
  48         int                      p_done:1;      /* 1 if res_done called */
  49         int                      p_verbose:1;   /* 1 means print errors */
  50         void                    *p_addrp;       /* address of mapped file */
  51         long                     p_size;        /* size of mapped file */
  52         struct cache_usage      *p_cusagep;     /* ptr to cache_usage */
  53         struct cachefs_rl_info  *p_linfop;      /* ptr to rl_info */
  54         rl_entry_t              *p_rlentp;      /* ptr to first rl_entry */
  55         int                      p_totentries;  /* max number of rl entries */
  56         char             p_name[MAXPATHLEN];    /* name of resource file */
  57 };
  58 
  59 #define MAGIC 8272
  60 #define precond(A) assert(A)
  61 #define MININDEX 1
  62 
  63 #define RL_HEAD(resp, type) \
  64         (&(resp->p_linfop->rl_items[CACHEFS_RL_INDEX(type)]))
  65 #define CVBLKS(nbytes) ((nbytes + MAXBSIZE - 1) / MAXBSIZE)
  66 
  67 /* forward references */
  68 void res_rlent_moveto(res *resp, enum cachefs_rl_type type, uint_t entno,
  69     long blks);
  70 void res_reset(res *resp);
  71 void res_clear(res *resp);
  72 int res_listcheck(res *, enum cachefs_rl_type);
  73 
  74 /*
  75  *
  76  *                      res_create
  77  *
  78  * Description:
  79  *      Creates a res object and returns a pointer to it.
  80  *      The specified file is used to store resource file data.
  81  * Arguments:
  82  *      namep   name of the resource file
  83  *      entries max number of rl entries in the file
  84  *      verbose 1 means print out error messages
  85  * Returns:
  86  *      Returns a pointer to the object or NULL if an error occurred.
  87  * Preconditions:
  88  *      precond(namep)
  89  *      precond(entries > 3)
  90  *      precond(strlen(namep) < MAXPATHLEN)
  91  */
  92 
  93 res *
  94 res_create(char *namep, int entries, int verbose)
  95 {
  96         int xx;
  97         long size;
  98         int fd;
  99         char buf[1024];
 100         long cnt;
 101         unsigned int amt;
 102         ssize_t result;
 103         void *addrp;
 104         res *resp;
 105         struct stat64 statinfo;
 106 
 107         precond(namep);
 108         precond(entries > MININDEX);
 109 
 110         /* determine the size needed for the resource file */
 111         size = MAXBSIZE;
 112         size += MAXBSIZE * (entries / CACHEFS_RLPMBS);
 113         if ((entries %  CACHEFS_RLPMBS) != 0)
 114                 size += MAXBSIZE;
 115 
 116         /* if the file does not exist or is the wrong size/type */
 117         xx = lstat64(namep, &statinfo);
 118         /* resource file will be <2GB */
 119         if ((xx == -1) || (statinfo.st_size != (offset_t)size) ||
 120             !(S_ISREG(statinfo.st_mode))) {
 121 
 122                 /* remove the resource file */
 123                 xx = unlink(namep);
 124                 if ((xx == -1) && (errno != ENOENT))
 125                         return (NULL);
 126 
 127                 /* create and open the file */
 128                 fd = open(namep, O_CREAT | O_RDWR, 0600);
 129                 if (fd == -1)
 130                         return (NULL);
 131 
 132                 /* fill the file with zeros */
 133                 memset(buf, 0, sizeof (buf));
 134                 for (cnt = size; cnt > 0; cnt -= result) {
 135                         amt = sizeof (buf);
 136                         if (amt > cnt)
 137                                 amt = cnt;
 138                         result = write(fd, buf, amt);
 139                         if (result == -1) {
 140                                 close(fd);
 141                                 return (NULL);
 142                         }
 143                 }
 144         }
 145 
 146         /* else open the file */
 147         else {
 148                 fd = open(namep, O_RDWR);
 149                 if (fd == -1)
 150                         return (NULL);
 151         }
 152 
 153         /* mmap the file into our address space */
 154         addrp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 155         if (addrp == (void *)-1) {
 156                 close(fd);
 157                 return (NULL);
 158         }
 159 
 160         /* close the file descriptor, we do not need it anymore */
 161         close(fd);
 162 
 163         /* allocate memory for the res object */
 164         resp = malloc(sizeof (res));
 165         if (resp == NULL) {
 166                 munmap(addrp, size);
 167                 return (NULL);
 168         }
 169 
 170         /* initialize the object */
 171         resp->p_magic = MAGIC;
 172         resp->p_done = 0;
 173         resp->p_addrp = addrp;
 174         resp->p_size = size;
 175         resp->p_verbose = verbose;
 176         resp->p_cusagep = (struct cache_usage *)addrp;
 177         resp->p_linfop = (struct cachefs_rl_info *)((char *)addrp +
 178             sizeof (struct cache_usage));
 179         resp->p_rlentp = (rl_entry_t *)((char *)addrp + MAXBSIZE);
 180         resp->p_totentries = entries;
 181         strcpy(resp->p_name, namep);
 182 
 183         /* reset the resource file in preperation to rebuild it */
 184         res_reset(resp);
 185 
 186         /* return the object */
 187         return (resp);
 188 }
 189 
 190 /*
 191  *
 192  *                      res_destroy
 193  *
 194  * Description:
 195  *      Destroys the specifed res object.
 196  *      If res_done has not been called on the object or if res_done
 197  *      failed, then the resource file will be deleted.
 198  * Arguments:
 199  *      resp    object to destroy
 200  * Returns:
 201  * Preconditions:
 202  *      precond(resp is a valid res object)
 203  */
 204 
 205 void
 206 res_destroy(res *resp)
 207 {
 208         precond(resp);
 209         precond(resp->p_magic == MAGIC);
 210 
 211         /* unmap the file */
 212         munmap(resp->p_addrp, resp->p_size);
 213 
 214         /* if res_done not performed */
 215         if (resp->p_done == 0) {
 216                 /* remove the resource file */
 217                 unlink(resp->p_name);
 218         }
 219 
 220         /* destroy the object */
 221         resp->p_magic = -MAGIC;
 222         free(resp);
 223 }
 224 
 225 rl_entry_t *
 226 res_rlent_get(res *resp, uint_t entno)
 227 {
 228         rl_entry_t *rlentp, *window;
 229         uint_t whichwindow, winoffset;
 230 
 231         precond((entno >= MININDEX) && (entno < resp->p_totentries));
 232 
 233         whichwindow = entno / CACHEFS_RLPMBS;
 234         winoffset = entno % CACHEFS_RLPMBS;
 235 
 236         window = (rl_entry_t *)
 237             (((caddr_t)resp->p_rlentp) + (MAXBSIZE * whichwindow));
 238         rlentp = window + winoffset;
 239 
 240         return (rlentp);
 241 }
 242 
 243 /*
 244  *
 245  *                      res_reset
 246  *
 247  * Description:
 248  *      Resets the resource file in preparation to rebuild it.
 249  * Arguments:
 250  *      resp    res object
 251  * Returns:
 252  * Preconditions:
 253  *      precond(resp is a valid res object)
 254  */
 255 
 256 void
 257 res_reset(res *resp)
 258 {
 259         int index;
 260         rl_entry_t *rlentp;
 261         int ret;
 262         cachefs_rl_listhead_t *lhp;
 263 
 264         precond(resp);
 265         precond(resp->p_magic == MAGIC);
 266 
 267         resp->p_cusagep->cu_blksused = 0;
 268         resp->p_cusagep->cu_filesused = 0;
 269         resp->p_cusagep->cu_flags = CUSAGE_ACTIVE;        /* dirty cache */
 270 
 271         /* clear out the non-pointer info */
 272         for (index = MININDEX; index < resp->p_totentries; index++) {
 273                 rlentp = res_rlent_get(resp, index);
 274 
 275                 rlentp->rl_attrc = 0;
 276                 rlentp->rl_fsck = 0;
 277                 rlentp->rl_local = 0;
 278                 rlentp->rl_fsid = 0LL;
 279                 rlentp->rl_fileno = 0;
 280         }
 281 
 282         /* verify validity of the various lists */
 283         ret = res_listcheck(resp, CACHEFS_RL_GC);
 284         if (ret == 1) {
 285                 ret = res_listcheck(resp, CACHEFS_RL_ATTRFILE);
 286                 if (ret == 1) {
 287                         ret = res_listcheck(resp, CACHEFS_RL_MODIFIED);
 288                         if (ret == 1) {
 289                                 ret = res_listcheck(resp, CACHEFS_RL_PACKED);
 290                                 if (ret == 1) {
 291                                         ret = res_listcheck(resp,
 292                                             CACHEFS_RL_PACKED_PENDING);
 293                                 }
 294                         }
 295                 }
 296         }
 297 
 298         /* if an error occurred on one of the lists */
 299         if (ret == 0) {
 300                 res_clear(resp);
 301                 return;
 302         }
 303 
 304         /* zero out total sizes, they get fixed up as we add items */
 305         RL_HEAD(resp, CACHEFS_RL_GC)->rli_blkcnt = 0;
 306         RL_HEAD(resp, CACHEFS_RL_ATTRFILE)->rli_blkcnt = 0;
 307         RL_HEAD(resp, CACHEFS_RL_MODIFIED)->rli_blkcnt = 0;
 308         RL_HEAD(resp, CACHEFS_RL_PACKED)->rli_blkcnt = 0;
 309         RL_HEAD(resp, CACHEFS_RL_PACKED_PENDING)->rli_blkcnt = 0;
 310 
 311         /* null out the heads of the lists we do not want to preserve */
 312         lhp = RL_HEAD(resp, CACHEFS_RL_FREE);
 313         memset(lhp, 0, sizeof (cachefs_rl_listhead_t));
 314         lhp = RL_HEAD(resp, CACHEFS_RL_NONE);
 315         memset(lhp, 0, sizeof (cachefs_rl_listhead_t));
 316         lhp = RL_HEAD(resp, CACHEFS_RL_MF);
 317         memset(lhp, 0, sizeof (cachefs_rl_listhead_t));
 318         lhp = RL_HEAD(resp, CACHEFS_RL_ACTIVE);
 319         memset(lhp, 0, sizeof (cachefs_rl_listhead_t));
 320 }
 321 
 322 /*
 323  *
 324  *                      res_listcheck
 325  *
 326  * Description:
 327  *      Checks the specified list.
 328  * Arguments:
 329  *      resp    res object
 330  *      type    list to check
 331  * Returns:
 332  *      Returns 1 if the list is ok, 0 if there is a problem.
 333  * Preconditions:
 334  *      precond(resp is a valid res object)
 335  */
 336 
 337 int
 338 res_listcheck(res *resp, enum cachefs_rl_type type)
 339 {
 340         rl_entry_t *rlentp;
 341         int previndex, index;
 342         cachefs_rl_listhead_t *lhp;
 343         int itemcnt = 0;
 344 
 345         lhp = RL_HEAD(resp, type);
 346         index = lhp->rli_front;
 347         previndex = 0;
 348 
 349         /* walk the list */
 350         while (index != 0) {
 351                 itemcnt++;
 352 
 353                 /* make sure offset is in bounds */
 354                 if ((index < MININDEX) || (index >= resp->p_totentries)) {
 355                         if (resp->p_verbose)
 356                                 pr_err("index out of bounds %d", index);
 357                         return (0);
 358                 }
 359 
 360                 /* get pointer to rl_entry object */
 361                 rlentp = res_rlent_get(resp, index);
 362 
 363                 /* check forward pointer */
 364                 if (rlentp->rl_fwd_idx != previndex) {
 365                         /* bad back pointer in rl list */
 366                         if (resp->p_verbose)
 367                                 pr_err(gettext("bad forward pointer %d %d"),
 368                                     rlentp->rl_fwd_idx, previndex);
 369                         return (0);
 370                 }
 371 
 372                 /* check for cycle */
 373                 if (rlentp->rl_fsck) {
 374                         /* cycle found in list */
 375                         if (resp->p_verbose)
 376                                 pr_err(gettext("cycle found in list %d"),
 377                                     index);
 378                         return (0);
 379                 }
 380 
 381                 /* check type */
 382                 if (rlentp->rl_current != type) {
 383                         /* entry doesn't belong here */
 384                         if (resp->p_verbose)
 385                                 pr_err(gettext(
 386                                     "bad entry %d type %d in list type %d"),
 387                                     index, (int)rlentp->rl_current, (int)type);
 388                         return (0);
 389                 }
 390 
 391                 /* indicate we have seen this pointer */
 392                 rlentp->rl_fsck = 1;
 393                 previndex = index;
 394                 index = rlentp->rl_bkwd_idx;
 395         }
 396 
 397         /* verify number of items match */
 398         if (itemcnt != lhp->rli_itemcnt) {
 399                 if (resp->p_verbose)
 400                         pr_err(gettext("itemcnt wrong old %d  new %d"),
 401                             lhp->rli_itemcnt, itemcnt);
 402                 return (0);
 403         }
 404 
 405         return (1);
 406 }
 407 
 408 /*
 409  *
 410  *                      res_clear
 411  *
 412  * Description:
 413  *      Deletes all information from the resource file.
 414  * Arguments:
 415  *      resp    res object
 416  * Returns:
 417  * Preconditions:
 418  *      precond(resp is a valid res object)
 419  */
 420 
 421 void
 422 res_clear(res *resp)
 423 {
 424         memset(resp->p_addrp, 0, resp->p_size);
 425 }
 426 
 427 
 428 /*
 429  *
 430  *                      res_done
 431  *
 432  * Description:
 433  *      Called when through performing res_addfile and res_addident
 434  *      to complete the resource file and flush the contents to
 435  *      the disk file.
 436  * Arguments:
 437  *      resp    res object
 438  * Returns:
 439  *      Returns 0 for success, -1 for an error with errno set
 440  *      appropriatly.
 441  * Preconditions:
 442  *      precond(resp is a valid res object)
 443  */
 444 
 445 int
 446 res_done(res *resp)
 447 {
 448         rl_entry_t *rlentp;
 449         int index;
 450         int xx;
 451         int ret;
 452 
 453         precond(resp);
 454         precond(resp->p_magic == MAGIC);
 455 
 456         /* scan the ident list to find the max allocated entry */
 457         resp->p_linfop->rl_entries = 0;
 458         for (index = MININDEX; index < resp->p_totentries; index++) {
 459                 rlentp = res_rlent_get(resp, index);
 460                 if (rlentp->rl_fsid && (ino64_t)rlentp->rl_fsck) {
 461                         resp->p_linfop->rl_entries = index;
 462                 }
 463         }
 464 
 465         /* scan the ident list to fix up the free list */
 466         for (index = MININDEX; index < resp->p_totentries; index++) {
 467                 rlentp = res_rlent_get(resp, index);
 468 
 469                 /* if entry is not valid */
 470                 if ((rlentp->rl_fsid == 0LL) || (rlentp->rl_fsck == 0)) {
 471                         /* if entry should appear on the free list */
 472                         if (index <= resp->p_linfop->rl_entries) {
 473                                 res_rlent_moveto(resp,
 474                                     CACHEFS_RL_FREE, index, 0);
 475                         }
 476                 }
 477                 rlentp->rl_fsck = 0; /* prepare to re-check */
 478         }
 479 
 480         /*
 481          * Sanity check that we do not have an internal error in
 482          * fsck.  Eventually turn this stuff off.
 483          */
 484 #if 1
 485         ret = res_listcheck(resp, CACHEFS_RL_GC);
 486         assert(ret == 1);
 487         ret = res_listcheck(resp, CACHEFS_RL_ATTRFILE);
 488         assert(ret == 1);
 489         ret = res_listcheck(resp, CACHEFS_RL_MODIFIED);
 490         assert(ret == 1);
 491         ret = res_listcheck(resp, CACHEFS_RL_PACKED);
 492         assert(ret == 1);
 493         ret = res_listcheck(resp, CACHEFS_RL_PACKED_PENDING);
 494         assert(ret == 1);
 495         ret = res_listcheck(resp, CACHEFS_RL_FREE);
 496         assert(ret == 1);
 497         ret = res_listcheck(resp, CACHEFS_RL_NONE);
 498         assert(ret == 1);
 499         ret = res_listcheck(resp, CACHEFS_RL_MF);
 500         assert(ret == 1);
 501         ret = res_listcheck(resp, CACHEFS_RL_ACTIVE);
 502         assert(ret == 1);
 503 #endif
 504 
 505         /* indicate the cache is clean */
 506         resp->p_cusagep->cu_flags &= ~CUSAGE_ACTIVE;
 507 
 508         /* sync the data to the file */
 509         xx = msync(resp->p_addrp, resp->p_size, MS_SYNC);
 510         if (xx == -1)
 511                 return (-1);
 512 
 513         resp->p_done = 1;
 514 
 515         /* return success */
 516         return (0);
 517 }
 518 
 519 /*
 520  *
 521  *                      res_addfile
 522  *
 523  * Description:
 524  *      Increments the number of files and blocks resource counts.
 525  * Arguments:
 526  *      resp    res object
 527  *      nbytes  number of bytes in the file
 528  * Returns:
 529  * Preconditions:
 530  *      precond(resp is a valid res object)
 531  */
 532 
 533 void
 534 res_addfile(res *resp, long nbytes)
 535 {
 536         precond(resp);
 537         precond(resp->p_magic == MAGIC);
 538 
 539         /* update resource counts */
 540         resp->p_cusagep->cu_blksused += CVBLKS(nbytes);
 541         resp->p_cusagep->cu_filesused += 1;
 542 }
 543 
 544 /*
 545  *
 546  *                      res_addident
 547  *
 548  * Description:
 549  *      Adds the specified file to the ident list.
 550  *      Updates resource counts.
 551  * Arguments:
 552  *      resp    res object
 553  *      index   index into idents/pointers tables
 554  *      dp      ident information
 555  *      nbytes  number of bytes of item
 556  *      file    number of files of item
 557  * Returns:
 558  *      Returns 0 for success or -1 if the index is already in use
 559  *      or is not valid.
 560  * Preconditions:
 561  *      precond(resp is a valid res object)
 562  *      precond(dp)
 563  */
 564 
 565 int
 566 res_addident(res *resp, int index, rl_entry_t *dp, long nbytes, int file)
 567 {
 568         rl_entry_t *rlentp;
 569 
 570         precond(resp);
 571         precond(resp->p_magic == MAGIC);
 572         precond(dp);
 573 
 574         /* check index for sanity */
 575         if ((index < MININDEX) || (index >= resp->p_totentries)) {
 576                 return (-1);
 577         }
 578 
 579         /* get pointer to ident */
 580         rlentp = res_rlent_get(resp, index);
 581 
 582         /* if something already there */
 583         if (rlentp->rl_fsid != 0LL) {
 584                 return (-1);
 585         }
 586 
 587         /* if not on the right list, move it there */
 588         if ((rlentp->rl_fsck == 0) || (rlentp->rl_current != dp->rl_current))
 589                 res_rlent_moveto(resp, dp->rl_current, index, CVBLKS(nbytes));
 590 
 591         rlentp->rl_fsck = 1;
 592         rlentp->rl_local = dp->rl_local;
 593         rlentp->rl_attrc = dp->rl_attrc;
 594         rlentp->rl_fsid = dp->rl_fsid;
 595         rlentp->rl_fileno = dp->rl_fileno;
 596 
 597         /* update resource counts */
 598         resp->p_cusagep->cu_blksused += CVBLKS(nbytes);
 599         resp->p_cusagep->cu_filesused += file;
 600 
 601         /* return success */
 602         return (0);
 603 }
 604 
 605 /*
 606  *
 607  *                      res_clearident
 608  *
 609  * Description:
 610  *      Removes the specified file from the ident list.
 611  *      Updates resource counts.
 612  * Arguments:
 613  *      resp    res object
 614  *      index   index into idents/pointers tables
 615  *      nbytes  number of bytes in the file
 616  *      file    number of files
 617  * Returns:
 618  *      Returns 0.
 619  * Preconditions:
 620  *      precond(resp is a valid res object)
 621  *      precond(index is valid)
 622  *      precond(ident is in use)
 623  */
 624 
 625 void
 626 res_clearident(res *resp, int index, int nbytes, int file)
 627 {
 628         rl_entry_t *rlentp;
 629 
 630         precond(resp);
 631         precond(resp->p_magic == MAGIC);
 632         precond((index >= MININDEX) && (index < resp->p_totentries));
 633 
 634         /* get pointer to ident */
 635         rlentp = res_rlent_get(resp, index);
 636         precond(rlentp->rl_fsid != 0LL);
 637 
 638         /* clear the ident */
 639         rlentp->rl_fsid = 0LL;
 640         rlentp->rl_fileno = 0;
 641         rlentp->rl_attrc = 0;
 642         rlentp->rl_local = 0;
 643 
 644         /* update resource counts */
 645         resp->p_cusagep->cu_blksused -= CVBLKS(nbytes);
 646         resp->p_cusagep->cu_filesused -= file;
 647         assert(resp->p_cusagep->cu_blksused >= 0);
 648 }
 649 
 650 /*
 651  * This function moves an RL entry from whereever it currently is to
 652  * the requested list.
 653  */
 654 
 655 void
 656 res_rlent_moveto(res *resp, enum cachefs_rl_type type, uint_t entno, long blks)
 657 {
 658         rl_entry_t *rl_ent;
 659         uint_t prev, next;
 660         cachefs_rl_listhead_t *lhp;
 661         enum cachefs_rl_type otype;
 662 
 663         precond((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
 664         precond((entno >= MININDEX) && (entno < resp->p_totentries));
 665 
 666         rl_ent = res_rlent_get(resp, entno);
 667         if (rl_ent->rl_fsck) {
 668                 /* remove entry from its previous list */
 669 
 670                 next = rl_ent->rl_fwd_idx;
 671                 prev = rl_ent->rl_bkwd_idx;
 672                 otype = rl_ent->rl_current;
 673                 assert((CACHEFS_RL_START <= otype) &&
 674                     (otype <= CACHEFS_RL_END));
 675 
 676                 lhp = RL_HEAD(resp, otype);
 677                 if ((lhp->rli_back == 0) || (lhp->rli_front == 0))
 678                         assert((lhp->rli_back == 0) && (lhp->rli_front == 0));
 679 
 680                 if (lhp->rli_back == entno)
 681                         lhp->rli_back = next;
 682                 if (lhp->rli_front == entno)
 683                         lhp->rli_front = prev;
 684                 if (prev != 0) {
 685                         rl_ent = res_rlent_get(resp, prev);
 686                         rl_ent->rl_fwd_idx = next;
 687                 }
 688                 if (next != 0) {
 689                         rl_ent = res_rlent_get(resp, next);
 690                         rl_ent->rl_bkwd_idx = prev;
 691                 }
 692                 lhp->rli_blkcnt -= blks;
 693                 lhp->rli_itemcnt--;
 694         }
 695 
 696         /* add entry to its new list */
 697 
 698         lhp = RL_HEAD(resp, type);
 699         rl_ent = res_rlent_get(resp, entno);
 700         rl_ent->rl_current = type;
 701         rl_ent->rl_bkwd_idx = 0;
 702         rl_ent->rl_fwd_idx = lhp->rli_back;
 703 
 704         if (lhp->rli_back != 0) {
 705                 assert(lhp->rli_front != 0);
 706                 rl_ent = res_rlent_get(resp, lhp->rli_back);
 707                 rl_ent->rl_bkwd_idx = entno;
 708         } else {
 709                 assert(lhp->rli_front == 0);
 710                 lhp->rli_front = entno;
 711         }
 712         lhp->rli_back = entno;
 713         lhp->rli_blkcnt += blks;
 714         lhp->rli_itemcnt++;
 715 
 716         rl_ent = res_rlent_get(resp, entno);
 717         rl_ent->rl_current = type;
 718         rl_ent->rl_fsck = 1;
 719 }