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 } 1366