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