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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 #pragma ident   "%Z%%M% %I%     %E% SMI"
  26 
  27 #include <sys/param.h>
  28 #include <sys/types.h>
  29 #include <sys/systm.h>
  30 #include <sys/cred.h>
  31 #include <sys/proc.h>
  32 #include <sys/user.h>
  33 #include <sys/vfs.h>
  34 #include <sys/vnode.h>
  35 #include <sys/pathname.h>
  36 #include <sys/uio.h>
  37 #include <sys/tiuser.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/kmem.h>
  40 #include <sys/ioctl.h>
  41 #include <sys/statvfs.h>
  42 #include <sys/errno.h>
  43 #include <sys/debug.h>
  44 #include <sys/cmn_err.h>
  45 #include <sys/utsname.h>
  46 #include <sys/modctl.h>
  47 #include <sys/dirent.h>
  48 #include <sys/fbuf.h>
  49 #include <rpc/types.h>
  50 #include <vm/seg.h>
  51 #include <vm/faultcode.h>
  52 #include <vm/hat.h>
  53 #include <vm/seg_map.h>
  54 #include <sys/fs/cachefs_fs.h>
  55 #include <sys/fs/cachefs_dir.h>
  56 #include <sys/fs/cachefs_log.h>
  57 
  58 /* forward declarations */
  59 static int cachefs_dir_getentrys(struct cnode *, u_offset_t, u_offset_t *,
  60     uint_t *, uint_t, caddr_t, int *);
  61 static int cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
  62     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep);
  63 static int cachefs_dir_extend(cnode_t *, u_offset_t *, int incr_frontblks);
  64 static int cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
  65     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize);
  66 static int cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp,
  67     vnode_t *frontvp, cred_t *cr, int acltoo);
  68 
  69 
  70 
  71 /*
  72  * cachefs_dir_look() called mainly by lookup (and create), looks up the cached
  73  * directory for an entry and returns the information there. If the directory
  74  * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL.
  75  * Should only call this routine if the dir is populated.
  76  * Returns ENOTDIR if dir gets nuked because of front file problems.
  77  */
  78 int
  79 cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp,
  80     u_offset_t *d_offsetp, cfs_cid_t *cidp)
  81 {
  82         int error;
  83         struct vattr va;
  84         u_offset_t blockoff = 0LL;
  85         uint_t offset = 0; /* offset inside the block of size MAXBSIZE */
  86         vnode_t *dvp;
  87         struct fscache *fscp = C_TO_FSCACHE(dcp);
  88         cachefscache_t *cachep = fscp->fs_cache;
  89         int nmlen;
  90         struct fbuf *fbp;
  91 
  92 #ifdef CFSDEBUG
  93         CFS_DEBUG(CFSDEBUG_DIR)
  94                 printf("cachefs_dir_look: ENTER dcp %p nm %s\n", (void *)dcp,
  95                                                                         nm);
  96 #endif
  97         ASSERT(CTOV(dcp)->v_type == VDIR);
  98         ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
  99         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 100 
 101         if (dcp->c_frontvp == NULL)
 102                 (void) cachefs_getfrontfile(dcp);
 103         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
 104                 error = ENOTDIR;
 105                 goto out;
 106         }
 107 
 108         dvp = dcp->c_frontvp;
 109         va.va_mask = AT_SIZE;           /* XXX should save dir size */
 110         error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
 111         if (error) {
 112                 cachefs_inval_object(dcp);
 113                 error = ENOTDIR;
 114                 goto out;
 115         }
 116 
 117         ASSERT(va.va_size != 0LL);
 118         nmlen = (int)strlen(nm);
 119         while (blockoff < va.va_size) {
 120                 offset = 0;
 121                 error =
 122                     fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
 123                 if (error)
 124                         goto out;
 125 
 126                 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
 127                         struct c_dirent *dep;
 128 
 129                         dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
 130                                                                 offset);
 131                         if ((dep->d_flag & CDE_VALID) &&
 132                                 (nmlen == dep->d_namelen) &&
 133                                 strcmp(dep->d_name, nm) == 0) {
 134                                 if (dep->d_flag & CDE_COMPLETE) {
 135                                         if (cookiep) {
 136                                                 CACHEFS_FID_COPY(&dep->d_cookie,
 137                                                         cookiep);
 138                                         }
 139                                         if (flagp)
 140                                                 *flagp = dep->d_flag;
 141                                         error = 0;
 142                                 } else {
 143                                         error = EINVAL;
 144                                 }
 145                                 if (cidp)
 146                                         *cidp = dep->d_id;
 147                                 if (d_offsetp)
 148                                         *d_offsetp = offset + blockoff;
 149                                 fbrelse(fbp, S_OTHER);
 150                                 goto out;
 151                         }
 152                         ASSERT(dep->d_length != 0);
 153                         offset += dep->d_length;
 154                 }
 155                 fbrelse(fbp, S_OTHER);
 156                 blockoff += MAXBSIZE;
 157         }
 158         error = ENOENT;
 159 
 160 out:
 161         if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
 162                 cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
 163                     &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
 164 #ifdef CFSDEBUG
 165         CFS_DEBUG(CFSDEBUG_DIR)
 166                 printf("c_dir_look: EXIT error = %d\n", error);
 167 #endif
 168         return (error);
 169 }
 170 
 171 /*
 172  * creates a new directory and populates it with "." and ".."
 173  */
 174 int
 175 cachefs_dir_new(cnode_t *dcp, cnode_t *cp)
 176 {
 177         int             error = 0;
 178         struct c_dirent *dep;
 179         u_offset_t      size;
 180         int len;
 181         struct fbuf     *fbp;
 182 #ifdef CFSDEBUG
 183         struct vattr    va;
 184 
 185         CFS_DEBUG(CFSDEBUG_DIR)
 186                 printf("c_dir_new: ENTER dcp %p cp %p\n", (void *)dcp,
 187                                                         (void *)cp);
 188 #endif
 189 
 190         ASSERT(MUTEX_HELD(&cp->c_statelock));
 191         ASSERT(CTOV(cp)->v_type == VDIR);
 192         ASSERT((cp->c_flags & CN_ASYNC_POPULATE) == 0);
 193         ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
 194 
 195         if (cp->c_frontvp == NULL) {
 196                 error = cachefs_getfrontfile(cp);
 197                 if (error)
 198                         goto out;
 199         }
 200 
 201 #ifdef CFSDEBUG
 202         va.va_mask = AT_SIZE;
 203         error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred, NULL);
 204         if (error)
 205                 goto out;
 206         ASSERT(va.va_size == 0);
 207 #endif
 208 
 209         /*
 210          * Extend the directory by one MAXBSIZE chunk
 211          */
 212         size = 0LL;
 213         error = cachefs_dir_extend(cp, &size, 1);
 214         if (error != 0)
 215                 goto out;
 216         error = fbread(cp->c_frontvp, (offset_t)0, MAXBSIZE, S_OTHER, &fbp);
 217         if (error)
 218                 goto out;
 219 
 220         /*
 221          * Insert "." and ".."
 222          */
 223         len = (int)CDE_SIZE(".");
 224         dep = (struct c_dirent *)fbp->fb_addr;
 225         dep->d_length = len;
 226         dep->d_offset = (offset_t)len;
 227         dep->d_flag = CDE_VALID | CDE_COMPLETE;
 228         CACHEFS_FID_COPY(&cp->c_cookie, &dep->d_cookie);
 229         dep->d_id = cp->c_id;
 230         dep->d_namelen = 1;
 231         bcopy(".", dep->d_name, 2);
 232 
 233         dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + len);
 234         dep->d_length = MAXBSIZE - len;
 235         dep->d_offset = MAXBSIZE;
 236         dep->d_flag = CDE_VALID | CDE_COMPLETE;
 237         CACHEFS_FID_COPY(&dcp->c_cookie, &dep->d_cookie);
 238         dep->d_id = dcp->c_id;
 239         dep->d_namelen = 2;
 240         bcopy("..", dep->d_name, 3);
 241 
 242         (void) fbdwrite(fbp);
 243 #ifdef INVALREADDIR
 244         cp->c_metadata.md_flags |= MD_POPULATED | MD_INVALREADDIR;
 245 #else
 246         cp->c_metadata.md_flags |= MD_POPULATED;
 247 #endif
 248         cp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
 249 out:
 250 #ifdef CFSDEBUG
 251         CFS_DEBUG(CFSDEBUG_DIR)
 252                 printf("cachefs_dir_new: EXIT error = %d\n", error);
 253 #endif
 254         return (error);
 255 }
 256 
 257 /*
 258  * cachefs_dir_enter adds a new directory entry. Takes as input a fid,
 259  * fileno and a sync flag. Most of the time, the caller is content with the
 260  * write to the (front) directory being done async. The exception being - for
 261  * local files, we should make sure that the directory entry is made
 262  * synchronously. That is notified by the caller.
 263  *              issync == 0 || issync == SM_ASYNC !
 264  *
 265  * The new entry is inserted at the end, so that we can generate local offsets
 266  * which are compatible with the backfs offsets (which are used when
 267  * disconnected.
 268  */
 269 int
 270 cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp,
 271     int issync)
 272 {
 273         struct vattr    va;
 274         int offset;
 275         u_offset_t blockoff = 0LL;
 276         u_offset_t      prev_offset;
 277         int             error = 0;
 278         vnode_t         *dvp;
 279         struct c_dirent *dep;
 280         uint_t          esize;
 281         u_offset_t      dirsize;
 282         struct fbuf     *fbp;
 283 
 284 #ifdef CFSDEBUG
 285         CFS_DEBUG(CFSDEBUG_DIR)
 286                 printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
 287                         (void *)dcp, nm, dcp->c_metadata.md_flags);
 288 #endif
 289 
 290         ASSERT(MUTEX_HELD(&dcp->c_statelock));
 291         ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
 292         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 293         ASSERT(CTOV(dcp)->v_type == VDIR);
 294         ASSERT(issync == 0 || issync == SM_ASYNC);
 295         ASSERT(strlen(nm) <= MAXNAMELEN);
 296 
 297         if (dcp->c_frontvp == NULL)
 298                 (void) cachefs_getfrontfile(dcp);
 299         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
 300                 error = ENOTDIR;
 301                 goto out;
 302         }
 303         dvp = dcp->c_frontvp;
 304 
 305         /*
 306          * Get the current EOF for the directory(data file)
 307          */
 308         va.va_mask = AT_SIZE;
 309         error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
 310         if (error) {
 311                 cachefs_inval_object(dcp);
 312                 error = ENOTDIR;
 313                 goto out;
 314         }
 315 
 316         /*
 317          * Get the last block of the directory
 318          */
 319         dirsize = va.va_size;
 320         ASSERT(dirsize != 0LL);
 321         ASSERT(!(dirsize & MAXBOFFSET));
 322         ASSERT(dirsize <= MAXOFF_T);
 323         blockoff = dirsize - MAXBSIZE;
 324         error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
 325         if (error)
 326                 goto out;
 327 
 328         /*
 329          * Find the last entry
 330          */
 331         offset = 0;
 332         prev_offset = blockoff;
 333         for (;;) {
 334                 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + offset);
 335                 if (offset + dep->d_length == MAXBSIZE)
 336                         break;
 337                 prev_offset = dep->d_offset;
 338                 offset += dep->d_length;
 339                 ASSERT(offset < MAXBSIZE);
 340         }
 341         esize = C_DIRSIZ(dep);
 342 
 343         if (dep->d_length - esize >= CDE_SIZE(nm)) {
 344                 /*
 345                  * It has room. If the entry is not valid, we can just use
 346                  * it. Otherwise, we need to adjust its length and offset
 347                  */
 348 #ifdef CFSDEBUG
 349                 CFS_DEBUG(CFSDEBUG_DIR) {
 350                         if (prev_offset >= dep->d_offset) {
 351                                 printf("cachefs_dir_enter: looks like "
 352                                     "we might fail the assert\n");
 353                                 printf("addr %p, offset %x, "
 354                                     "prev_offset %llx, dep->d_offset %llx\n",
 355                                     (void *)fbp->fb_addr, offset, prev_offset,
 356                                     dep->d_offset);
 357                                 offset = 0;
 358                                 prev_offset = blockoff;
 359                                 for (;;) {
 360                                         dep = (struct c_dirent *)
 361                                             ((uintptr_t)fbp->fb_addr + offset);
 362                                         printf("offset %x, prev_offset %llx\n",
 363                                             offset, prev_offset);
 364                                         printf("dep->d_offset %llx, "
 365                                             "dep->d_length %x\n",
 366                                             dep->d_offset, dep->d_length);
 367                                         if (offset + dep->d_length == MAXBSIZE)
 368                                                 break;
 369                                         prev_offset = dep->d_offset;
 370                                         offset += dep->d_length;
 371                                 }
 372                         }
 373                 }
 374 #endif /* CFSDEBUG */
 375 
 376                 if (offset)
 377                         ASSERT(prev_offset < dep->d_offset);
 378                 if (dep->d_flag & CDE_VALID) {
 379                         dep->d_length = esize;
 380                         dep->d_offset = prev_offset + (u_offset_t)esize;
 381                         dep = (struct c_dirent *)((uintptr_t)dep + esize);
 382                 }
 383                 dep->d_length = (int)((offset_t)MAXBSIZE -
 384                                 ((uintptr_t)dep - (uintptr_t)fbp->fb_addr));
 385         } else {
 386                 /*
 387                  * No room - so extend the file by one more
 388                  * MAXBSIZE chunk, and fit the entry there.
 389                  */
 390                 fbrelse(fbp, S_OTHER);
 391                 error = cachefs_dir_extend(dcp, &dirsize, 1);
 392                 if (error != 0)
 393                         goto out;
 394                 error =
 395                     fbread(dvp, (offset_t)va.va_size, MAXBSIZE, S_OTHER, &fbp);
 396                 if (error)
 397                         goto out;
 398                 dep = (struct c_dirent *)fbp->fb_addr;
 399                 dep->d_length = MAXBSIZE;
 400         }
 401 
 402         /*
 403          * Fill in the rest of the new entry
 404          */
 405         dep->d_offset = dirsize;
 406         dep->d_flag = CDE_VALID;
 407         if (cookiep) {
 408                 dep->d_flag |= CDE_COMPLETE;
 409                 CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
 410         }
 411         dep->d_id = *cidp;
 412         dep->d_namelen = (ushort_t)strlen(nm);
 413         (void) bcopy(nm, dep->d_name, dep->d_namelen + 1);
 414 
 415 #ifdef INVALREADDIR
 416         dcp->c_metadata.md_flags |= MD_INVALREADDIR;
 417 #endif
 418         dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
 419         if (issync)
 420                 (void) fbwrite(fbp);
 421         else
 422                 (void) fbdwrite(fbp);
 423 out:
 424 #ifdef CFSDEBUG
 425         CFS_DEBUG(CFSDEBUG_DIR)
 426                 printf("cachefs_dir_enter: EXIT error = %d\n", error);
 427 #endif
 428         return (error);
 429 }
 430 
 431 /*
 432  * Quite simple, if the deleted entry is the first in the MAXBSIZE block,
 433  * we simply mark it invalid. Otherwise, the deleted entries d_length is
 434  * just added to the previous entry.
 435  */
 436 int
 437 cachefs_dir_rmentry(cnode_t *dcp, char *nm)
 438 {
 439         u_offset_t blockoff = 0LL;
 440         int offset = 0;
 441         struct vattr va;
 442         int error = ENOENT;
 443         vnode_t *dvp;
 444         int nmlen;
 445         struct fbuf *fbp;
 446 
 447 #ifdef CFSDEBUG
 448         CFS_DEBUG(CFSDEBUG_DIR)
 449                 printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
 450                     (void *)dcp, nm);
 451 #endif
 452         ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
 453         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 454 
 455         if (dcp->c_frontvp == NULL)
 456                 (void) cachefs_getfrontfile(dcp);
 457         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
 458                 error = ENOTDIR;
 459                 goto out;
 460         }
 461         dvp = dcp->c_frontvp;
 462 
 463         ASSERT(CTOV(dcp)->v_type == VDIR);
 464         ASSERT((dcp->c_flags & CN_NOCACHE) == 0);
 465         ASSERT(dvp != NULL);
 466         va.va_mask = AT_SIZE;
 467         error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
 468         if (error) {
 469                 cachefs_inval_object(dcp);
 470                 error = ENOTDIR;
 471                 goto out;
 472         }
 473         ASSERT(va.va_size != 0LL);
 474 
 475         nmlen = (int)strlen(nm);
 476         while (blockoff < va.va_size) {
 477                 uint_t *last_len;
 478 
 479                 offset = 0;
 480                 last_len = NULL;
 481                 error =
 482                     fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
 483                 if (error)
 484                         goto out;
 485                 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
 486                         struct c_dirent *dep;
 487 
 488                         dep  = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
 489                                                                 offset);
 490                         if ((dep->d_flag & CDE_VALID) &&
 491                                 (nmlen == dep->d_namelen) &&
 492                                 strcmp(dep->d_name, nm) == 0) {
 493                                 /*
 494                                  * Found the entry. If this was the first entry
 495                                  * in the MAXBSIZE block, Mark it invalid. Else
 496                                  * add it's length to the previous entry's
 497                                  * length.
 498                                  */
 499                                 if (last_len == NULL) {
 500                                         ASSERT(offset == 0);
 501                                         dep->d_flag = 0;
 502                                 } else
 503                                         *last_len += dep->d_length;
 504                                 (void) fbdwrite(fbp);
 505                                 dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
 506                                 goto out;
 507                         }
 508                         last_len = &dep->d_length;
 509                         offset += dep->d_length;
 510                 }
 511                 fbrelse(fbp, S_OTHER);
 512                 blockoff += MAXBSIZE;
 513         }
 514         error = ENOENT;
 515 
 516 out:
 517 #ifdef CFSDEBUG
 518         CFS_DEBUG(CFSDEBUG_DIR)
 519                 printf("cachefs_dir_rmentry: EXIT error = %d\n", error);
 520 #endif
 521         return (error);
 522 }
 523 
 524 #if 0
 525 /*
 526  * This function is used only in cachefs_lookup_back() routine but
 527  * is inside #if 0 directive in this routine. So I am keeping this
 528  * routine also in #if 0 directive.
 529  */
 530 
 531 /*
 532  * This function fills in the cookie and file no of the directory entry
 533  * at the offset specified by offset - In other words, makes the entry
 534  * "complete".
 535  */
 536 int
 537 cachefs_dir_modentry(cnode_t *dcp, u_offset_t offset, fid_t *cookiep,
 538     cfs_cid_t *cidp)
 539 {
 540         struct c_dirent *dep;
 541         u_offset_t blockoff = (offset & (offset_t)MAXBMASK);
 542         uint_t off = (uint_t)(offset & (offset_t)MAXBOFFSET);
 543         struct fbuf *fbp;
 544         vnode_t *dvp;
 545         int error = 0;
 546 
 547 #ifdef CFSDEBUG
 548         CFS_DEBUG(CFSDEBUG_DIR)
 549                 printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
 550                         (void *)dcp, offset);
 551 #endif
 552         ASSERT(CTOV(dcp)->v_type == VDIR);
 553         ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
 554         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 555 
 556         if (dcp->c_frontvp == NULL)
 557                 (void) cachefs_getfrontfile(dcp);
 558         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
 559                 return;
 560         }
 561         dvp = dcp->c_frontvp;
 562 
 563         error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
 564         if (error)
 565                 goto out;
 566         dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
 567         if (cookiep) {
 568                 dep->d_flag |= CDE_COMPLETE;
 569                 CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
 570         }
 571         if (cidp)
 572                 dep->d_id = *cidp;
 573         (void) fbdwrite(fbp);
 574         dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
 575 
 576 out:
 577 #ifdef CFSDEBUG
 578         CFS_DEBUG(CFSDEBUG_DIR)
 579                 printf("cachefs_dir_modentry: EXIT\n");
 580 #endif
 581         return (error);
 582 }
 583 
 584 #endif  /* of #if 0 */
 585 
 586 /*
 587  * Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
 588  * packs them into buf.
 589  */
 590 static int
 591 cachefs_dir_getentrys(struct cnode *dcp, u_offset_t beg_off,
 592                 u_offset_t *last_offp, uint_t *cntp, uint_t bufsize,
 593                                 caddr_t buf, int *eofp)
 594 {
 595 
 596 #define DIR_ENDOFF      0x7fffffffLL
 597 
 598         struct vattr va;
 599         struct c_dirent *dep;
 600         struct fbuf *fbp = NULL;
 601         struct dirent64 *gdp;
 602         u_offset_t blockoff;
 603         uint_t off;
 604         int error;
 605         vnode_t *dvp = dcp->c_frontvp;
 606 
 607 #ifdef CFSDEBUG
 608         CFS_DEBUG(CFSDEBUG_DIR)
 609         printf("cachefs_dir_getentrys: "
 610             "ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
 611             dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags);
 612 #endif
 613 
 614         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 615 
 616         /*
 617          * blockoff has the offset of the MAXBSIZE block that contains the
 618          * entry  to start with. off contains the offset relative to the
 619          * begining of the MAXBSIZE block.
 620          */
 621         if (eofp)
 622                 *eofp = 0;
 623         gdp = (struct dirent64 *)buf;
 624         *cntp = bufsize;
 625         va.va_mask = AT_SIZE;
 626         error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
 627         if (error) {
 628                 *cntp = 0;
 629                 *last_offp = 0;
 630                 if (eofp)
 631                         *eofp = 1;
 632                 goto out;
 633         }
 634         ASSERT(va.va_size != 0LL);
 635 
 636         if (beg_off == DIR_ENDOFF) {
 637                 *cntp = 0;
 638                 *last_offp = DIR_ENDOFF;
 639                 if (eofp)
 640                         *eofp = 1;
 641                 goto out;
 642         }
 643 
 644         /*
 645          * locate the offset where we start reading.
 646          */
 647         for (blockoff = 0; blockoff < va.va_size; blockoff += MAXBSIZE) {
 648                 error =
 649                     fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
 650                 if (error)
 651                         goto out;
 652                 dep = (struct c_dirent *)fbp->fb_addr;
 653                 off = 0;
 654                 while (off < MAXBSIZE && dep->d_offset <= beg_off) {
 655                         off += dep->d_length;
 656                         dep =
 657                             (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
 658                 }
 659                 if (off < MAXBSIZE)
 660                         break;
 661                 fbrelse(fbp, S_OTHER);
 662                 fbp = NULL;
 663         }
 664 
 665         if (blockoff >= va.va_size) {
 666                 *cntp = 0;
 667                 *last_offp = DIR_ENDOFF;
 668                 if (eofp)
 669                         *eofp = 1;
 670                 goto out;
 671         }
 672 
 673         /*
 674          * Just load up the buffer with directory entries.
 675          */
 676         for (;;) {
 677                 uint_t size;
 678                 int this_reclen;
 679 
 680                 ASSERT((uintptr_t)dep < ((uintptr_t)fbp->fb_addr + MAXBSIZE));
 681                 if (dep->d_flag & CDE_VALID) {
 682                         this_reclen = DIRENT64_RECLEN(dep->d_namelen);
 683                         size = C_DIRSIZ(dep);
 684                         ASSERT(size < MAXBSIZE);
 685                         if (this_reclen > bufsize)
 686                                 break;
 687                         ASSERT(dep->d_namelen <= MAXNAMELEN);
 688                         ASSERT(dep->d_offset > (*last_offp));
 689                         gdp->d_ino = dep->d_id.cid_fileno;
 690                         gdp->d_off = dep->d_offset;
 691 
 692                         /* use strncpy(9f) to zero out uninitialized bytes */
 693 
 694                         ASSERT(strlen(dep->d_name) + 1 <=
 695                             DIRENT64_NAMELEN(this_reclen));
 696                         (void) strncpy(gdp->d_name, dep->d_name,
 697                             DIRENT64_NAMELEN(this_reclen));
 698 
 699                         gdp->d_reclen = (ushort_t)this_reclen;
 700                         bufsize -= this_reclen;
 701                         gdp = (struct dirent64 *)((uintptr_t)gdp +
 702                                 gdp->d_reclen);
 703                         *last_offp = dep->d_offset;
 704                 }
 705 
 706                 /*
 707                  * Increment the offset. If we've hit EOF, fill in
 708                  * the lastoff and current entries d_off field.
 709                  */
 710                 off += dep->d_length;
 711                 ASSERT(off <= MAXBSIZE);
 712                 if ((blockoff + off) >= va.va_size) {
 713                         *last_offp = DIR_ENDOFF;
 714                         if (eofp)
 715                                 *eofp = 1;
 716                         break;
 717                 }
 718                 /*
 719                  * If off == MAXBSIZE, then we need to adjust our
 720                  * window to the next MAXBSIZE block of the directory.
 721                  * Adjust blockoff, off and map it in. Also, increment
 722                  * the directory and buffer pointers.
 723                  */
 724                 if (off == MAXBSIZE) {
 725                         fbrelse(fbp, S_OTHER);
 726                         fbp = NULL;
 727                         off = 0;
 728                         blockoff += MAXBSIZE;
 729                         error = fbread(dvp, (offset_t)blockoff, MAXBSIZE,
 730                                                                 S_OTHER, &fbp);
 731                         if (error)
 732                                 goto out;
 733                 }
 734                 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
 735         }
 736         *cntp -= bufsize;
 737 out:
 738         /*
 739          * Release any buffer and maping that may exist.
 740          */
 741         if (fbp)
 742                 (void) fbrelse(fbp, S_OTHER);
 743 #ifdef CFSDEBUG
 744         CFS_DEBUG(CFSDEBUG_DIR)
 745                 printf("ccachefs_dir_getentrys: EXIT error = %d\n", error);
 746 #endif
 747         return (error);
 748 }
 749 
 750 /*
 751  * Called by cachefs_readdir(). Fills a directory request from the cache
 752  */
 753 int
 754 cachefs_dir_read(struct cnode *dcp, struct uio *uiop, int *eofp)
 755 {
 756         int error;
 757         uint_t count;
 758         uint_t size;
 759         caddr_t buf;
 760         u_offset_t next = uiop->uio_loffset;
 761         struct fscache *fscp = C_TO_FSCACHE(dcp);
 762         cachefscache_t *cachep = fscp->fs_cache;
 763         caddr_t chrp, end;
 764         dirent64_t *de;
 765 
 766         ASSERT(CTOV(dcp)->v_type == VDIR);
 767         ASSERT(RW_READ_HELD(&dcp->c_rwlock));
 768 
 769         ASSERT(next <= MAXOFF_T);
 770 #ifdef CFSDEBUG
 771         CFS_DEBUG(CFSDEBUG_DIR)
 772                 printf("cachefs_dir_read: ENTER dcp %p\n", (void *)dcp);
 773 #endif
 774         ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) ==
 775             (MD_FILE|MD_POPULATED));
 776         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
 777 
 778         if (dcp->c_frontvp == NULL)
 779                 (void) cachefs_getfrontfile(dcp);
 780         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
 781                 error = ENOTDIR;
 782                 goto out;
 783         }
 784 
 785         size = (uint_t)uiop->uio_resid;
 786         buf = cachefs_kmem_alloc(size, KM_SLEEP);
 787         error = cachefs_dir_getentrys(dcp, next, &next, &count, size,
 788             buf, eofp);
 789         if (error == 0 && (int)count > 0) {
 790                 ASSERT(count <= size);
 791                 if (fscp->fs_inum_size > 0) {
 792                         ino64_t newinum;
 793 
 794                         mutex_exit(&dcp->c_statelock);
 795                         mutex_enter(&fscp->fs_fslock);
 796                         end = (caddr_t)((uintptr_t)buf + count);
 797                         for (chrp = buf; chrp < end; chrp += de->d_reclen) {
 798                                 de = (dirent64_t *)chrp;
 799 
 800                                 newinum = cachefs_inum_real2fake(fscp,
 801                                     de->d_ino);
 802                                 if (newinum == 0)
 803                                         newinum = cachefs_fileno_conflict(fscp,
 804                                             de->d_ino);
 805                                 de->d_ino = newinum;
 806                         }
 807                         mutex_exit(&fscp->fs_fslock);
 808                         mutex_enter(&dcp->c_statelock);
 809                 }
 810                 error = uiomove(buf, count, UIO_READ, uiop);
 811                 if (error == 0)
 812                         uiop->uio_loffset = next;
 813         }
 814         (void) cachefs_kmem_free(buf, size);
 815 out:
 816         if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
 817                 cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
 818                     &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
 819 #ifdef CFSDEBUG
 820         CFS_DEBUG(CFSDEBUG_DIR)
 821                 printf("cachefs_dir_read: EXIT error = %d\n", error);
 822 #endif
 823         return (error);
 824 }
 825 
 826 /*
 827  * Fully (including cookie) populates the directory from the back filesystem.
 828  */
 829 int
 830 cachefs_dir_fill(cnode_t *dcp, cred_t *cr)
 831 {
 832         int error = 0;
 833         u_offset_t frontsize;
 834         struct fscache *fscp = C_TO_FSCACHE(dcp);
 835 
 836 #ifdef CFSDEBUG
 837         CFS_DEBUG(CFSDEBUG_DIR)
 838                 printf("cachefs_dir_fill: ENTER dcp %p\n", (void *)dcp);
 839 #endif
 840         ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
 841         ASSERT(MUTEX_HELD(&dcp->c_statelock));
 842 
 843         /* XXX for now return success if async populate is scheduled */
 844         if (dcp->c_flags & CN_ASYNC_POPULATE)
 845                 goto out;
 846 
 847         /* get the back vp */
 848         if (dcp->c_backvp == NULL) {
 849                 error = cachefs_getbackvp(fscp, dcp);
 850                 if (error) {
 851                         goto out;
 852                 }
 853         }
 854 
 855         /* get the front file vp */
 856         if (dcp->c_frontvp == NULL)
 857                 (void) cachefs_getfrontfile(dcp);
 858         if (dcp->c_flags & CN_NOCACHE) {
 859                 error = ENOTDIR;
 860                 goto out;
 861         }
 862 
 863         /* if dir was modified, toss old contents */
 864         if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
 865                 cachefs_inval_object(dcp);
 866                 if (dcp->c_flags & CN_NOCACHE) {
 867                         error = ENOTDIR;
 868                         goto out;
 869                 }
 870         }
 871 
 872         error = cachefs_dir_fill_common(dcp, cr,
 873             dcp->c_frontvp, dcp->c_backvp, &frontsize);
 874         if (error == 0)
 875                 error = cachefs_dir_complete(fscp, dcp->c_backvp,
 876                     dcp->c_frontvp, cr, 0);
 877         if (error != 0)
 878                 goto out;
 879 
 880         /*
 881          * Mark the directory as not empty. Also bang the flag that says that
 882          * this directory needs to be sync'ed on inactive.
 883          */
 884         dcp->c_metadata.md_flags |= MD_POPULATED;
 885         dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
 886         dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
 887         /*LINTED alignment okay*/
 888         dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
 889 
 890 out:
 891         if (error) {
 892 #ifdef CFSDEBUG
 893                 CFS_DEBUG(CFSDEBUG_INVALIDATE)
 894                         printf("c_dir_fill: invalidating %llu\n",
 895                             (u_longlong_t)dcp->c_id.cid_fileno);
 896 #endif
 897                 cachefs_inval_object(dcp);
 898         }
 899 
 900         return (error);
 901 }
 902 
 903 /*
 904  * Does work of populating directory.
 905  * Must be called while holding dcp->c_statelock
 906  */
 907 
 908 static int
 909 cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
 910     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize)
 911 {
 912         int error = 0;
 913         struct uio uio;
 914         struct iovec iov;
 915         caddr_t buf = NULL;
 916         int count;
 917         int eof = 0;
 918         u_offset_t frontoff;
 919         struct fscache *fscp = C_TO_FSCACHE(dcp);
 920         cachefscache_t *cachep = fscp->fs_cache;
 921 #ifdef DEBUG
 922         int loop_count = 0;
 923 #endif
 924 #ifdef CFSDEBUG
 925         CFS_DEBUG(CFSDEBUG_DIR)
 926                 printf("cachefs_dir_fill_common: ENTER dcp %p\n", (void *)dcp);
 927 #endif
 928 
 929         ASSERT(MUTEX_HELD(&dcp->c_statelock));
 930 
 931         frontoff = *frontsize = 0LL;
 932 
 933         buf = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
 934         uio.uio_iov = &iov;
 935         uio.uio_iovcnt = 1;
 936         uio.uio_segflg = UIO_SYSSPACE;
 937         uio.uio_fmode = 0;
 938         uio.uio_extflg = UIO_COPY_CACHED;
 939         uio.uio_loffset = 0;
 940         for (;;) {
 941 #ifdef DEBUG
 942                 loop_count++;
 943 #endif
 944                 /*
 945                  * Read in a buffer's worth of dirents and enter them in to the
 946                  * directory.
 947                  */
 948                 uio.uio_resid = MAXBSIZE;
 949                 iov.iov_base = buf;
 950                 iov.iov_len = MAXBSIZE;
 951                 (void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL);
 952                 error = VOP_READDIR(backvp, &uio, cr, &eof, NULL, 0);
 953                 VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL);
 954                 if (error)
 955                         goto out;
 956 
 957                 /*LINTED alignment okay*/
 958                 count = MAXBSIZE - (int)uio.uio_resid;
 959                 ASSERT(count >= 0);
 960                 if (count > 0) {
 961                         if (error = cachefs_dir_stuff(dcp, count, buf,
 962                             frontvp, &frontoff, frontsize))
 963                                 goto out;
 964                         ASSERT((*frontsize) != 0LL);
 965                 }
 966                 if (eof || count == 0)
 967                         break;
 968         }
 969 
 970         if (*frontsize == 0LL) {
 971                 /* keep us from caching an empty directory */
 972                 error = EINVAL;
 973                 goto out;
 974         }
 975 
 976 out:
 977         if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR))
 978                 cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp,
 979                     &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
 980                     *frontsize);
 981         if (buf)
 982                 cachefs_kmem_free(buf, (uint_t)MAXBSIZE);
 983 
 984 #ifdef CFSDEBUG
 985         CFS_DEBUG(CFSDEBUG_DIR)
 986                 printf("cachefs_dir_fill: EXIT error = %d\n", error);
 987 #endif
 988         return (error);
 989 }
 990 
 991 /*
 992  * If the directory contains only the elements "." and "..", then this returns
 993  * 0, otherwise returns an error.
 994  */
 995 int
 996 cachefs_dir_empty(cnode_t *dcp)
 997 {
 998         struct vattr va;
 999         u_offset_t blockoff = 0;
1000         int offset;
1001         struct fbuf *fbp;
1002         int error;
1003         vnode_t *dvp = dcp->c_frontvp;
1004 
1005 #ifdef CFSDEBUG
1006         CFS_DEBUG(CFSDEBUG_DIR)
1007                 printf("cachefs_dir_empty: ENTER dcp %p\n", (void *)dcp);
1008 #endif
1009         ASSERT(CTOV(dcp)->v_type == VDIR);
1010         ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
1011         ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
1012 
1013         if (dcp->c_frontvp == NULL)
1014                 (void) cachefs_getfrontfile(dcp);
1015         if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)
1016                 return (ENOTDIR);
1017 
1018         va.va_mask = AT_SIZE;
1019         error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
1020         if (error)
1021                 return (ENOTDIR);
1022 
1023         ASSERT(va.va_size != 0LL);
1024         while (blockoff < va.va_size) {
1025                 offset = 0;
1026                 error =
1027                     fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1028                 if (error)
1029                         return (error);
1030                 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
1031                         struct c_dirent *dep;
1032 
1033                         dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
1034                                                                 offset);
1035                         if ((dep->d_flag & CDE_VALID) &&
1036                                 ((strcmp(dep->d_name, ".") != 0) &&
1037                                 (strcmp(dep->d_name, "..") != 0))) {
1038                                 (void) fbrelse(fbp, S_OTHER);
1039                                 return (0);
1040                         }
1041                         offset += dep->d_length;
1042                 }
1043                 (void) fbrelse(fbp, S_OTHER);
1044                 blockoff += MAXBSIZE;
1045         }
1046         return (EEXIST);
1047 }
1048 
1049 /*
1050  * Called by cachefs_dir_fill() to stuff a buffer of dir entries into
1051  * a front file.  This is more efficient than repeated calls to
1052  * cachefs_dir_enter, and it also allows us to maintain entries in backfs
1053  * order (readdir requires that entry offsets be ascending).
1054  */
1055 static int
1056 cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
1057     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep)
1058 {
1059         int error = 0;
1060         struct fbuf *fbp;
1061         struct c_dirent *cdep, *last;
1062         struct dirent64 *dep;
1063         int inblk, entsize;
1064         u_offset_t blockoff = (*offsetp & (offset_t)MAXBMASK);
1065         /*LINTED alignment okay*/
1066         uint_t off = (uint_t)(*offsetp & (offset_t)MAXBOFFSET);
1067 
1068         /*LINTED want count != 0*/
1069         ASSERT(count > 0);
1070 
1071         if (*offsetp >= *fsizep) {
1072                 error = cachefs_dir_extend(dcp, fsizep, 0);
1073                 if (error)
1074                         return (error);
1075         }
1076 
1077         ASSERT(*fsizep != 0LL);
1078         last = NULL;
1079         error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1080         if (error)
1081                 return (error);
1082         cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1083         inblk = MAXBSIZE-off;
1084         if (*offsetp != 0) {
1085                 ASSERT(cdep->d_length == inblk);
1086                 inblk -= C_DIRSIZ(cdep);
1087                 last = cdep;
1088                 last->d_length -= inblk;
1089                 off += last->d_length;
1090                 cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1091         }
1092         dep = (struct dirent64 *)buf;
1093         /*LINTED want count != 0*/
1094         while (count > 0) {
1095                 if (last) {
1096                         ASSERT(dep->d_off > last->d_offset);
1097                 }
1098                 entsize = (int)CDE_SIZE(dep->d_name);
1099                 if (entsize > inblk) {
1100                         if (last) {
1101                                 last->d_length += inblk;
1102                         }
1103                         (void) fbwrite(fbp);
1104                         error = cachefs_dir_extend(dcp, fsizep, 0);
1105                         if (error)
1106                                 return (error);
1107                         ASSERT(*fsizep != 0LL);
1108                         blockoff += MAXBSIZE;
1109                         error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE,
1110                                                         S_OTHER, &fbp);
1111                         if (error)
1112                                 return (error);
1113                         off = 0;
1114                         cdep = (struct c_dirent *)fbp->fb_addr;
1115                         inblk = MAXBSIZE;
1116                         last = NULL;
1117                 }
1118                 cdep->d_length = entsize;
1119                 cdep->d_id.cid_fileno = dep->d_ino;
1120                 cdep->d_id.cid_flags = 0;
1121                 cdep->d_namelen = (ushort_t)strlen(dep->d_name);
1122                 cdep->d_flag = CDE_VALID;
1123                 bcopy(dep->d_name, cdep->d_name, cdep->d_namelen+1);
1124                 cdep->d_offset = dep->d_off;
1125                 inblk -= entsize;
1126                 count -= dep->d_reclen;
1127                 dep = (struct dirent64 *)((uintptr_t)dep + dep->d_reclen);
1128                 *offsetp = blockoff + (u_offset_t)off;
1129                 off += entsize;
1130                 last = cdep;
1131                 cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1132         }
1133         if (last) {
1134                 last->d_length += inblk;
1135         }
1136         (void) fbwrite(fbp);
1137         return (error);
1138 }
1139 
1140 static int
1141 cachefs_dir_extend(cnode_t *dcp, u_offset_t *cursize, int incr_frontblks)
1142 {
1143         struct vattr va;
1144         cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache;
1145         int error = 0;
1146         struct fscache *fscp = VFS_TO_FSCACHE(CTOV(dcp)->v_vfsp);
1147 
1148         ASSERT(MUTEX_HELD(&dcp->c_statelock));
1149         ASSERT(((*cursize) & (MAXBSIZE-1)) == 0);
1150 
1151         va.va_mask = AT_SIZE;
1152         va.va_size = (u_offset_t)(*cursize + MAXBSIZE);
1153         error = cachefs_allocblocks(cachep, 1, dcp->c_metadata.md_rltype);
1154         if (error)
1155                 return (error);
1156         error = VOP_SETATTR(dcp->c_frontvp, &va, 0, kcred, NULL);
1157         if (error) {
1158                 cachefs_freeblocks(cachep, 1, dcp->c_metadata.md_rltype);
1159                 return (error);
1160         }
1161         if (incr_frontblks)
1162                 dcp->c_metadata.md_frontblks++;
1163         if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1164                 dcp->c_size += MAXBSIZE;
1165                 dcp->c_attr.va_size = dcp->c_size;
1166         }
1167         *cursize += MAXBSIZE;
1168         ASSERT(*cursize != 0LL);
1169         if (incr_frontblks)
1170                 dcp->c_flags |= CN_UPDATED;
1171         return (0);
1172 }
1173 
1174 int
1175 cachefs_async_populate_dir(struct cachefs_populate_req *pop, cred_t *cr,
1176     vnode_t *backvp, vnode_t *frontvp)
1177 {
1178         vnode_t *dvp = pop->cpop_vp;
1179         struct cnode *dcp = VTOC(dvp);
1180         u_offset_t frontsize;
1181         int error = 0;
1182 
1183 #ifdef CFSDEBUG
1184         CFS_DEBUG(CFSDEBUG_DIR)
1185                 printf("cachefs_async_populate_dir: ENTER dvp %p\n",
1186                                                                 (void *)dvp);
1187 #endif
1188         ASSERT(MUTEX_HELD(&dcp->c_statelock));
1189         ASSERT(dvp->v_type == VDIR);
1190         ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0);
1191         ASSERT(dcp->c_frontvp == frontvp);
1192         ASSERT(dcp->c_backvp == backvp);
1193         ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
1194 
1195         /* if dir was modified, toss old contents */
1196         if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
1197                 cachefs_inval_object(dcp);
1198                 if (dcp->c_flags & CN_NOCACHE) {
1199                         error = ENOTDIR;
1200                         goto out;
1201                 } else {
1202                         dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
1203                 }
1204         }
1205 
1206 
1207         error = cachefs_dir_fill_common(dcp, cr, frontvp, backvp, &frontsize);
1208         if (error != 0)
1209                 goto out;
1210         ASSERT(frontsize != 0LL);
1211         mutex_exit(&dcp->c_statelock);
1212         /*
1213          * I don't like to break lock here but cachefs_dir_complete()
1214          * needs it.
1215          */
1216         error = cachefs_dir_complete(C_TO_FSCACHE(dcp), backvp,
1217             frontvp, cr, 1);
1218         mutex_enter(&dcp->c_statelock);
1219         if (error != 0)
1220                 goto out;
1221         /* if went nocache while lock was dropped, get out */
1222         if ((dcp->c_flags & CN_NOCACHE) || (dcp->c_frontvp == NULL)) {
1223                 error = EINVAL;
1224         } else {
1225                 /* allocfile and allocblocks have already happened. */
1226                 dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
1227         }
1228 
1229 out:
1230 
1231 #ifdef CFSDEBUG
1232         CFS_DEBUG(CFSDEBUG_DIR)
1233                 printf("cachefs_async_populate_dir: EXIT error = %d\n", error);
1234 #endif
1235 
1236         return (error);
1237 }
1238 
1239 static int
1240 cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp,
1241     cred_t *cr, int acltoo)
1242 {
1243         struct c_dirent *dep;
1244         caddr_t buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
1245         struct vattr va;
1246         u_offset_t blockoff;
1247         int offset;
1248         u_offset_t dir_size;
1249         struct fbuf *fbp;
1250         cnode_t *cp;
1251         fid_t cookie;
1252         vnode_t *entry_vp;
1253         int error = 0;
1254 
1255         /*
1256          * note: caller better not hold a c_statelock if acltoo is set.
1257          */
1258 
1259         va.va_mask = AT_SIZE;
1260         error = VOP_GETATTR(frontvp, &va, 0, cr, NULL);
1261         if (error)
1262                 goto out;
1263 
1264         ASSERT(va.va_size != 0LL);
1265         dir_size = va.va_size;
1266         ASSERT(dir_size <= MAXOFF_T);
1267 
1268         for (blockoff = 0; blockoff < dir_size; blockoff += MAXBSIZE) {
1269                 if (error = fbread(frontvp, (offset_t)blockoff,
1270                     MAXBSIZE, S_OTHER, &fbp))
1271                         goto out;
1272 
1273                 /*
1274                  * We cannot hold any page locks across the below VOP
1275                  * operations. We thus copy the directory entries into a
1276                  * staging buffer, and release the page lock on the directory
1277                  * by calling fbrelse().  Once any necessary cnodes have
1278                  * been created, we'll reacquire the page lock with fbread()
1279                  * and copy the staging buffer back into the frontvp at
1280                  * blockoff.
1281                  */
1282                 bcopy(fbp->fb_addr, buf, MAXBSIZE);
1283                 fbrelse(fbp, S_OTHER);
1284 
1285                 for (offset = 0;
1286                     offset < MAXBSIZE &&
1287                     (blockoff + (u_offset_t)offset) < dir_size;
1288                     offset += dep->d_length) {
1289 
1290                         dep = (struct c_dirent *)((uintptr_t)buf + offset);
1291                         ASSERT(dep->d_length != 0);
1292                         if ((dep->d_flag & (CDE_VALID | CDE_COMPLETE)) !=
1293                             CDE_VALID)
1294                                 continue;
1295 
1296                         error = VOP_LOOKUP(backvp, dep->d_name,
1297                             &entry_vp, (struct pathname *)NULL, 0,
1298                             (vnode_t *)NULL, cr, NULL, NULL, NULL);
1299                         if (error) {
1300                                 /* lookup on .. in / on coc gets ENOENT */
1301                                 if (error == ENOENT) {
1302                                         error = 0;
1303                                         continue;
1304                                 }
1305                                 goto out;
1306                         }
1307 
1308                         error = cachefs_getcookie(entry_vp, &cookie, NULL, cr,
1309                                 TRUE);
1310                         if (error) {
1311 #ifdef CFSDEBUG
1312                                 CFS_DEBUG(CFSDEBUG_DIR)
1313                                         printf("\t%s: getcookie error\n",
1314                                             dep->d_name);
1315 #endif /* CFSDEBUG */
1316                                 VN_RELE(entry_vp);
1317                                 goto out;
1318                         }
1319                         CACHEFS_FID_COPY(&cookie, &dep->d_cookie);
1320                         dep->d_flag |= CDE_COMPLETE;
1321 
1322                         if ((! acltoo) ||
1323                             (! cachefs_vtype_aclok(entry_vp)) ||
1324                             (fscp->fs_info.fi_mntflags & CFS_NOACL)) {
1325                                 VN_RELE(entry_vp);
1326                                 continue;
1327                         }
1328 
1329                         error = cachefs_cnode_make(&dep->d_id, fscp, &cookie,
1330                             NULL, entry_vp, cr, 0, &cp);
1331                         VN_RELE(entry_vp);
1332                         if (error != 0)
1333                                 goto out;
1334 
1335                         ASSERT(cp != NULL);
1336                         mutex_enter(&cp->c_statelock);
1337 
1338                         if ((cp->c_flags & CN_NOCACHE) ||
1339                             (cp->c_metadata.md_flags & MD_ACL)) {
1340                                 mutex_exit(&cp->c_statelock);
1341                                 VN_RELE(CTOV(cp));
1342                                 continue;
1343                         }
1344 
1345                         (void) cachefs_cacheacl(cp, NULL);
1346                         mutex_exit(&cp->c_statelock);
1347                         VN_RELE(CTOV(cp));
1348                 }
1349 
1350                 /*
1351                  * We must now re-lock the page corresponding to the frontvp,
1352                  * and copy our staging buffer onto it.
1353                  */
1354                 if (error = fbread(frontvp, (offset_t)blockoff,
1355                     MAXBSIZE, S_OTHER, &fbp))
1356                         goto out;
1357 
1358                 bcopy(buf, fbp->fb_addr, MAXBSIZE);
1359                 (void) fbdwrite(fbp);
1360         }
1361 
1362 out:
1363         kmem_free(buf, MAXBSIZE);
1364         return (error);
1365 }