1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <strsafe.h> 24 #include <time.h> 25 #include <assert.h> 26 27 #include "nfs41_ops.h" 28 #include "nfs41_compound.h" 29 #include "name_cache.h" 30 #include "util.h" 31 #include "tree.h" 32 #include "daemon_debug.h" 33 34 35 /* dprintf levels for name cache logging */ 36 enum { 37 NCLVL1 = 2, 38 NCLVL2 39 }; 40 41 42 #define NAME_CACHE_EXPIRATION 20 /* TODO: get from configuration */ 43 44 /* allow up to 256K of memory for name and attribute cache entries */ 45 #define NAME_CACHE_MAX_SIZE 262144 46 47 /* negative lookup caching 48 * 49 * by caching lookups that result in NOENT, we can avoid sending subsequent 50 * lookups over the wire. a name cache entry is negative when its attributes 51 * pointer is NULL. negative entries are created by three functions: 52 * nfs41_name_cache_remove(), _insert() when called with NULL for the fh and 53 * attributes, and _rename() for the source entry */ 54 55 /* delegations and cache feedback 56 * 57 * delegations provide a guarantee that no links or attributes will change 58 * without notice. the name cache takes advantage of this by preventing 59 * delegated entries from being removed on NAME_CACHE_EXPIRATION, though 60 * they're still removed when a parent is invalidated. the attribute cache 61 * holds an extra reference on delegated entries to prevent their removal 62 * entirely, until the delegation is returned. 63 * this extra reference presents a problem when the number of delegations 64 * approaches the maximum number of attribute cache entries. when there are 65 * not enough available entries to store the parent directories, every lookup 66 * results in a name cache miss, and cache performance degrades significantly. 67 * the solution is to provide feedback via nfs41_name_cache_insert() when 68 * delegations reach a certain percent of the cache capacity. the error code 69 * ERROR_TOO_MANY_OPEN_FILES, chosen arbitrarily for this case, instructs the 70 * caller to return an outstanding delegation before caching a new one. 71 */ 72 static __inline bool_t is_delegation( 73 IN enum open_delegation_type4 type) 74 { 75 return type == OPEN_DELEGATE_READ || type == OPEN_DELEGATE_WRITE; 76 } 77 78 79 /* attribute cache */ 80 struct attr_cache_entry { 81 RB_ENTRY(attr_cache_entry) rbnode; 82 struct list_entry free_entry; 83 uint64_t change; 84 uint64_t size; 85 uint64_t fileid; 86 int64_t time_access_s; 87 int64_t time_create_s; 88 int64_t time_modify_s; 89 uint32_t time_access_ns; 90 uint32_t time_create_ns; 91 uint32_t time_modify_ns; 92 uint32_t numlinks; 93 unsigned mode : 30; 94 unsigned hidden : 1; 95 unsigned system : 1; 96 unsigned archive : 1; 97 time_t expiration; 98 unsigned ref_count : 26; 99 unsigned type : 4; 100 unsigned invalidated : 1; 101 unsigned delegated : 1; 102 }; 103 #define ATTR_ENTRY_SIZE sizeof(struct attr_cache_entry) 104 105 RB_HEAD(attr_tree, attr_cache_entry); 106 107 struct attr_cache { 108 struct attr_tree head; 109 struct attr_cache_entry *pool; 110 struct list_entry free_entries; 111 }; 112 113 int attr_cmp(struct attr_cache_entry *lhs, struct attr_cache_entry *rhs) 114 { 115 return lhs->fileid < rhs->fileid ? -1 : lhs->fileid > rhs->fileid; 116 } 117 RB_GENERATE(attr_tree, attr_cache_entry, rbnode, attr_cmp) 118 119 120 /* attr_cache_entry */ 121 #define attr_entry(pos) list_container(pos, struct attr_cache_entry, free_entry) 122 123 static int attr_cache_entry_create( 124 IN struct attr_cache *cache, 125 IN uint64_t fileid, 126 OUT struct attr_cache_entry **entry_out) 127 { 128 struct attr_cache_entry *entry; 129 int status = NO_ERROR; 130 131 /* get the next entry from free_entries and remove it */ 132 if (list_empty(&cache->free_entries)) { 133 status = ERROR_OUTOFMEMORY; 134 goto out; 135 } 136 entry = attr_entry(cache->free_entries.next); 137 list_remove(&entry->free_entry); 138 139 entry->fileid = fileid; 140 entry->invalidated = FALSE; 141 entry->delegated = FALSE; 142 *entry_out = entry; 143 out: 144 return status; 145 } 146 147 static __inline void attr_cache_entry_free( 148 IN struct attr_cache *cache, 149 IN struct attr_cache_entry *entry) 150 { 151 dprintf(NCLVL1, "attr_cache_entry_free(%llu)\n", entry->fileid); 152 RB_REMOVE(attr_tree, &cache->head, entry); 153 /* add it back to free_entries */ 154 list_add_tail(&cache->free_entries, &entry->free_entry); 155 } 156 157 static __inline void attr_cache_entry_ref( 158 IN struct attr_cache *cache, 159 IN struct attr_cache_entry *entry) 160 { 161 const uint32_t previous = entry->ref_count++; 162 dprintf(NCLVL2, "attr_cache_entry_ref(%llu) %u -> %u\n", 163 entry->fileid, previous, entry->ref_count); 164 } 165 166 static __inline void attr_cache_entry_deref( 167 IN struct attr_cache *cache, 168 IN struct attr_cache_entry *entry) 169 { 170 const uint32_t previous = entry->ref_count--; 171 dprintf(NCLVL2, "attr_cache_entry_deref(%llu) %u -> %u\n", 172 entry->fileid, previous, entry->ref_count); 173 174 if (entry->ref_count == 0) 175 attr_cache_entry_free(cache, entry); 176 } 177 178 static __inline int attr_cache_entry_expired( 179 IN const struct attr_cache_entry *entry) 180 { 181 return entry->invalidated || 182 (!entry->delegated && time(NULL) > entry->expiration); 183 } 184 185 /* attr_cache */ 186 static int attr_cache_init( 187 IN struct attr_cache *cache, 188 IN uint32_t max_entries) 189 { 190 uint32_t i; 191 int status = NO_ERROR; 192 193 /* allocate a pool of entries */ 194 cache->pool = calloc(max_entries, ATTR_ENTRY_SIZE); 195 if (cache->pool == NULL) { 196 status = GetLastError(); 197 goto out; 198 } 199 200 /* initialize the list of free entries */ 201 list_init(&cache->free_entries); 202 for (i = 0; i < max_entries; i++) { 203 list_init(&cache->pool[i].free_entry); 204 list_add_tail(&cache->free_entries, &cache->pool[i].free_entry); 205 } 206 out: 207 return status; 208 } 209 210 static void attr_cache_free( 211 IN struct attr_cache *cache) 212 { 213 /* free the pool */ 214 free(cache->pool); 215 cache->pool = NULL; 216 list_init(&cache->free_entries); 217 } 218 219 static struct attr_cache_entry* attr_cache_search( 220 IN struct attr_cache *cache, 221 IN uint64_t fileid) 222 { 223 /* find an entry that matches fileid */ 224 struct attr_cache_entry tmp; 225 tmp.fileid = fileid; 226 return RB_FIND(attr_tree, &cache->head, &tmp); 227 } 228 229 static int attr_cache_insert( 230 IN struct attr_cache *cache, 231 IN struct attr_cache_entry *entry) 232 { 233 int status = NO_ERROR; 234 235 dprintf(NCLVL2, "--> attr_cache_insert(%llu)\n", entry->fileid); 236 237 if (RB_INSERT(attr_tree, &cache->head, entry)) 238 status = ERROR_FILE_EXISTS; 239 240 dprintf(NCLVL2, "<-- attr_cache_insert() returning %d\n", status); 241 return status; 242 } 243 244 static int attr_cache_find_or_create( 245 IN struct attr_cache *cache, 246 IN uint64_t fileid, 247 OUT struct attr_cache_entry **entry_out) 248 { 249 struct attr_cache_entry *entry; 250 int status = NO_ERROR; 251 252 dprintf(NCLVL1, "--> attr_cache_find_or_create(%llu)\n", fileid); 253 254 /* look for an existing entry */ 255 entry = attr_cache_search(cache, fileid); 256 if (entry == NULL) { 257 /* create and insert */ 258 status = attr_cache_entry_create(cache, fileid, &entry); 259 if (status) 260 goto out; 261 262 status = attr_cache_insert(cache, entry); 263 if (status) 264 goto out_err_free; 265 } 266 267 /* take a reference on success */ 268 attr_cache_entry_ref(cache, entry); 269 270 out: 271 *entry_out = entry; 272 dprintf(NCLVL1, "<-- attr_cache_find_or_create() returning %d\n", 273 status); 274 return status; 275 276 out_err_free: 277 attr_cache_entry_free(cache, entry); 278 entry = NULL; 279 goto out; 280 } 281 282 static void attr_cache_update( 283 IN struct attr_cache_entry *entry, 284 IN const nfs41_file_info *info, 285 IN enum open_delegation_type4 delegation) 286 { 287 /* update the attributes present in mask */ 288 if (info->attrmask.count >= 1) { 289 if (info->attrmask.arr[0] & FATTR4_WORD0_TYPE) 290 entry->type = (unsigned char)(info->type & NFS_FTYPE_MASK); 291 if (info->attrmask.arr[0] & FATTR4_WORD0_CHANGE) { 292 entry->change = info->change; 293 /* revalidate whenever we get a change attribute */ 294 entry->invalidated = 0; 295 entry->expiration = time(NULL) + NAME_CACHE_EXPIRATION; 296 } 297 if (info->attrmask.arr[0] & FATTR4_WORD0_SIZE) 298 entry->size = info->size; 299 if (info->attrmask.arr[0] & FATTR4_WORD0_HIDDEN) 300 entry->hidden = info->hidden; 301 if (info->attrmask.arr[0] & FATTR4_WORD0_ARCHIVE) 302 entry->archive = info->archive; 303 } 304 if (info->attrmask.count >= 2) { 305 if (info->attrmask.arr[1] & FATTR4_WORD1_MODE) 306 entry->mode = info->mode; 307 if (info->attrmask.arr[1] & FATTR4_WORD1_NUMLINKS) 308 entry->numlinks = info->numlinks; 309 if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_ACCESS) { 310 entry->time_access_s = info->time_access.seconds; 311 entry->time_access_ns = info->time_access.nseconds; 312 } 313 if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_CREATE) { 314 entry->time_create_s = info->time_create.seconds; 315 entry->time_create_ns = info->time_create.nseconds; 316 } 317 if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_MODIFY) { 318 entry->time_modify_s = info->time_modify.seconds; 319 entry->time_modify_ns = info->time_modify.nseconds; 320 } 321 if (info->attrmask.arr[1] & FATTR4_WORD1_SYSTEM) 322 entry->system = info->system; 323 } 324 325 if (is_delegation(delegation)) 326 entry->delegated = TRUE; 327 } 328 329 static void copy_attrs( 330 OUT nfs41_file_info *dst, 331 IN const struct attr_cache_entry *src) 332 { 333 dst->change = src->change; 334 dst->size = src->size; 335 dst->time_access.seconds = src->time_access_s; 336 dst->time_access.nseconds = src->time_access_ns; 337 dst->time_create.seconds = src->time_create_s; 338 dst->time_create.nseconds = src->time_create_ns; 339 dst->time_modify.seconds = src->time_modify_s; 340 dst->time_modify.nseconds = src->time_modify_ns; 341 dst->type = src->type; 342 dst->numlinks = src->numlinks; 343 dst->mode = src->mode; 344 dst->fileid = src->fileid; 345 dst->hidden = src->hidden; 346 dst->system = src->system; 347 dst->archive = src->archive; 348 349 dst->attrmask.count = 2; 350 dst->attrmask.arr[0] = FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE 351 | FATTR4_WORD0_SIZE | FATTR4_WORD0_FILEID 352 | FATTR4_WORD0_HIDDEN | FATTR4_WORD0_ARCHIVE; 353 dst->attrmask.arr[1] = FATTR4_WORD1_MODE 354 | FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_TIME_ACCESS 355 | FATTR4_WORD1_TIME_CREATE | FATTR4_WORD1_TIME_MODIFY 356 | FATTR4_WORD1_SYSTEM; 357 } 358 359 360 /* name cache */ 361 RB_HEAD(name_tree, name_cache_entry); 362 struct name_cache_entry { 363 char component[NFS41_MAX_COMPONENT_LEN]; 364 nfs41_fh fh; 365 RB_ENTRY(name_cache_entry) rbnode; 366 struct name_tree rbchildren; 367 struct attr_cache_entry *attributes; 368 struct name_cache_entry *parent; 369 struct list_entry exp_entry; 370 time_t expiration; 371 unsigned short component_len; 372 }; 373 #define NAME_ENTRY_SIZE sizeof(struct name_cache_entry) 374 375 int name_cmp(struct name_cache_entry *lhs, struct name_cache_entry *rhs) 376 { 377 const int diff = rhs->component_len - lhs->component_len; 378 return diff ? diff : strncmp(lhs->component, rhs->component, lhs->component_len); 379 } 380 RB_GENERATE(name_tree, name_cache_entry, rbnode, name_cmp) 381 382 struct nfs41_name_cache { 383 struct name_cache_entry *root; 384 struct name_cache_entry *pool; 385 struct attr_cache attributes; 386 struct list_entry exp_entries; /* list of entries by expiry */ 387 uint32_t expiration; 388 uint32_t entries; 389 uint32_t max_entries; 390 uint32_t delegations; 391 uint32_t max_delegations; 392 SRWLOCK lock; 393 }; 394 395 396 /* internal name cache functions used by the public name cache interface; 397 * these functions expect the caller to hold a lock on the cache */ 398 399 #define name_entry(pos) list_container(pos, struct name_cache_entry, exp_entry) 400 401 static __inline bool_t name_cache_enabled( 402 IN struct nfs41_name_cache *cache) 403 { 404 return cache->expiration > 0; 405 } 406 407 static __inline void name_cache_entry_rename( 408 OUT struct name_cache_entry *entry, 409 IN const nfs41_component *component) 410 { 411 StringCchCopyNA(entry->component, NFS41_MAX_COMPONENT_LEN, 412 component->name, component->len); 413 entry->component_len = component->len; 414 } 415 416 static __inline void name_cache_remove( 417 IN struct name_cache_entry *entry, 418 IN struct name_cache_entry *parent) 419 { 420 RB_REMOVE(name_tree, &parent->rbchildren, entry); 421 entry->parent = NULL; 422 } 423 424 static void name_cache_unlink_children_recursive( 425 IN struct nfs41_name_cache *cache, 426 IN struct name_cache_entry *parent); 427 428 static __inline void name_cache_unlink( 429 IN struct nfs41_name_cache *cache, 430 IN struct name_cache_entry *entry) 431 { 432 /* remove the entry from the tree */ 433 if (entry->parent) 434 name_cache_remove(entry, entry->parent); 435 else if (entry == cache->root) 436 cache->root = NULL; 437 438 /* unlink all of its children */ 439 name_cache_unlink_children_recursive(cache, entry); 440 /* release the cached attributes */ 441 if (entry->attributes) { 442 attr_cache_entry_deref(&cache->attributes, entry->attributes); 443 entry->attributes = NULL; 444 } 445 /* move it to the end of exp_entries for scavenging */ 446 list_remove(&entry->exp_entry); 447 list_add_tail(&cache->exp_entries, &entry->exp_entry); 448 } 449 450 static void name_cache_unlink_children_recursive( 451 IN struct nfs41_name_cache *cache, 452 IN struct name_cache_entry *parent) 453 { 454 struct name_cache_entry *entry, *tmp; 455 RB_FOREACH_SAFE(entry, name_tree, &parent->rbchildren, tmp) 456 name_cache_unlink(cache, entry); 457 } 458 459 static int name_cache_entry_create( 460 IN struct nfs41_name_cache *cache, 461 IN const nfs41_component *component, 462 OUT struct name_cache_entry **entry_out) 463 { 464 int status = NO_ERROR; 465 struct name_cache_entry *entry; 466 467 if (cache->entries >= cache->max_entries) { 468 /* scavenge the oldest entry */ 469 if (list_empty(&cache->exp_entries)) { 470 status = ERROR_OUTOFMEMORY; 471 goto out; 472 } 473 entry = name_entry(cache->exp_entries.prev); 474 name_cache_unlink(cache, entry); 475 476 dprintf(NCLVL2, "name_cache_entry_create('%s') scavenged 0x%p\n", 477 component->name, entry); 478 } else { 479 /* take the next entry in the pool and add it to exp_entries */ 480 entry = &cache->pool[cache->entries++]; 481 list_init(&entry->exp_entry); 482 list_add_tail(&cache->exp_entries, &entry->exp_entry); 483 } 484 485 name_cache_entry_rename(entry, component); 486 487 *entry_out = entry; 488 out: 489 return status; 490 } 491 492 static void name_cache_entry_accessed( 493 IN struct nfs41_name_cache *cache, 494 IN struct name_cache_entry *entry) 495 { 496 /* move the entry to the front of cache->exp_entries, then do 497 * the same for its parents, which are more costly to evict */ 498 while (entry) { 499 /* if entry is delegated, it won't be in the list */ 500 if (!list_empty(&entry->exp_entry)) { 501 list_remove(&entry->exp_entry); 502 list_add_head(&cache->exp_entries, &entry->exp_entry); 503 } 504 if (entry == entry->parent) 505 break; 506 entry = entry->parent; 507 } 508 } 509 510 static void name_cache_entry_updated( 511 IN struct nfs41_name_cache *cache, 512 IN struct name_cache_entry *entry) 513 { 514 /* update the expiration timer */ 515 entry->expiration = time(NULL) + cache->expiration; 516 name_cache_entry_accessed(cache, entry); 517 } 518 519 static int name_cache_entry_update( 520 IN struct nfs41_name_cache *cache, 521 IN struct name_cache_entry *entry, 522 IN OPTIONAL const nfs41_fh *fh, 523 IN OPTIONAL const nfs41_file_info *info, 524 IN enum open_delegation_type4 delegation) 525 { 526 int status = NO_ERROR; 527 528 if (fh) 529 fh_copy(&entry->fh, fh); 530 else 531 entry->fh.len = 0; 532 533 if (info) { 534 if (entry->attributes == NULL) { 535 /* negative -> positive entry, create the attributes */ 536 status = attr_cache_find_or_create(&cache->attributes, 537 info->fileid, &entry->attributes); 538 if (status) 539 goto out; 540 } 541 542 attr_cache_update(entry->attributes, info, delegation); 543 544 /* hold a reference as long as we have the delegation */ 545 if (is_delegation(delegation)) { 546 attr_cache_entry_ref(&cache->attributes, entry->attributes); 547 cache->delegations++; 548 } 549 550 /* keep the entry from expiring */ 551 if (entry->attributes->delegated) 552 list_remove(&entry->exp_entry); 553 } else if (entry->attributes) { 554 /* positive -> negative entry, deref the attributes */ 555 attr_cache_entry_deref(&cache->attributes, entry->attributes); 556 entry->attributes = NULL; 557 } 558 name_cache_entry_updated(cache, entry); 559 out: 560 return status; 561 } 562 563 static int name_cache_entry_changed( 564 IN struct nfs41_name_cache *cache, 565 IN struct name_cache_entry *entry, 566 IN const change_info4 *cinfo) 567 { 568 if (entry->attributes == NULL) 569 return FALSE; 570 571 if (cinfo->after == entry->attributes->change || 572 (cinfo->atomic && cinfo->before == entry->attributes->change)) { 573 entry->attributes->change = cinfo->after; 574 name_cache_entry_updated(cache, entry); 575 dprintf(NCLVL1, "name_cache_entry_changed('%s') has not changed. " 576 "updated change=%llu\n", entry->component, 577 entry->attributes->change); 578 return FALSE; 579 } else { 580 dprintf(NCLVL1, "name_cache_entry_changed('%s') has changed: was %llu, " 581 "got before=%llu\n", entry->component, 582 entry->attributes->change, cinfo->before); 583 return TRUE; 584 } 585 } 586 587 static void name_cache_entry_invalidate( 588 IN struct nfs41_name_cache *cache, 589 IN struct name_cache_entry *entry) 590 { 591 dprintf(NCLVL1, "name_cache_entry_invalidate('%s')\n", entry->component); 592 593 if (entry->attributes) { 594 /* flag attributes so that entry_invis() will return true 595 * if another entry attempts to use them */ 596 entry->attributes->invalidated = 1; 597 } 598 name_cache_unlink(cache, entry); 599 } 600 601 static struct name_cache_entry* name_cache_search( 602 IN struct nfs41_name_cache *cache, 603 IN struct name_cache_entry *parent, 604 IN const nfs41_component *component) 605 { 606 struct name_cache_entry tmp, *entry; 607 608 dprintf(NCLVL2, "--> name_cache_search('%.*s' under '%s')\n", 609 component->len, component->name, parent->component); 610 611 StringCchCopyNA(tmp.component, NFS41_MAX_COMPONENT_LEN, 612 component->name, component->len); 613 tmp.component_len = component->len; 614 615 entry = RB_FIND(name_tree, &parent->rbchildren, &tmp); 616 if (entry) 617 dprintf(NCLVL2, "<-- name_cache_search() " 618 "found existing entry 0x%p\n", entry); 619 else 620 dprintf(NCLVL2, "<-- name_cache_search() returning NULL\n"); 621 return entry; 622 } 623 624 static int entry_invis( 625 IN struct name_cache_entry *entry, 626 OUT OPTIONAL bool_t *is_negative) 627 { 628 /* name entry timer expired? */ 629 if (!list_empty(&entry->exp_entry) && time(NULL) > entry->expiration) { 630 dprintf(NCLVL2, "name_entry_expired('%s')\n", entry->component); 631 return 1; 632 } 633 /* negative lookup entry? */ 634 if (entry->attributes == NULL) { 635 if (is_negative) *is_negative = 1; 636 dprintf(NCLVL2, "name_entry_negative('%s')\n", entry->component); 637 return 1; 638 } 639 /* attribute entry expired? */ 640 if (attr_cache_entry_expired(entry->attributes)) { 641 dprintf(NCLVL2, "attr_entry_expired(%llu)\n", 642 entry->attributes->fileid); 643 return 1; 644 } 645 return 0; 646 } 647 648 static int name_cache_lookup( 649 IN struct nfs41_name_cache *cache, 650 IN bool_t skip_invis, 651 IN const char *path, 652 IN const char *path_end, 653 OUT OPTIONAL const char **remaining_path_out, 654 OUT OPTIONAL struct name_cache_entry **parent_out, 655 OUT OPTIONAL struct name_cache_entry **target_out, 656 OUT OPTIONAL bool_t *is_negative) 657 { 658 struct name_cache_entry *parent, *target; 659 nfs41_component component; 660 const char *path_pos; 661 int status = NO_ERROR; 662 663 dprintf(NCLVL1, "--> name_cache_lookup('%s')\n", path); 664 665 parent = NULL; 666 target = cache->root; 667 component.name = path_pos = path; 668 669 if (target == NULL || (skip_invis && entry_invis(target, is_negative))) { 670 target = NULL; 671 status = ERROR_PATH_NOT_FOUND; 672 goto out; 673 } 674 675 while (next_component(path_pos, path_end, &component)) { 676 parent = target; 677 target = name_cache_search(cache, parent, &component); 678 path_pos = component.name + component.len; 679 if (target == NULL || (skip_invis && entry_invis(target, is_negative))) { 680 target = NULL; 681 if (is_last_component(component.name, path_end)) 682 status = ERROR_FILE_NOT_FOUND; 683 else 684 status = ERROR_PATH_NOT_FOUND; 685 break; 686 } 687 } 688 out: 689 if (remaining_path_out) *remaining_path_out = component.name; 690 if (parent_out) *parent_out = parent; 691 if (target_out) *target_out = target; 692 dprintf(NCLVL1, "<-- name_cache_lookup() returning %d\n", status); 693 return status; 694 } 695 696 static int name_cache_insert( 697 IN struct name_cache_entry *entry, 698 IN struct name_cache_entry *parent) 699 { 700 int status = NO_ERROR; 701 702 dprintf(NCLVL2, "--> name_cache_insert('%s')\n", entry->component); 703 704 if (RB_INSERT(name_tree, &parent->rbchildren, entry)) 705 status = ERROR_FILE_EXISTS; 706 entry->parent = parent; 707 708 dprintf(NCLVL2, "<-- name_cache_insert() returning %u\n", status); 709 return status; 710 } 711 712 static int name_cache_find_or_create( 713 IN struct nfs41_name_cache *cache, 714 IN struct name_cache_entry *parent, 715 IN const nfs41_component *component, 716 OUT struct name_cache_entry **target_out) 717 { 718 int status = NO_ERROR; 719 720 dprintf(NCLVL1, "--> name_cache_find_or_create('%.*s' under '%s')\n", 721 component->len, component->name, parent->component); 722 723 *target_out = name_cache_search(cache, parent, component); 724 if (*target_out) 725 goto out; 726 727 status = name_cache_entry_create(cache, component, target_out); 728 if (status) 729 goto out; 730 731 status = name_cache_insert(*target_out, parent); 732 if (status) 733 goto out_err; 734 735 out: 736 dprintf(NCLVL1, "<-- name_cache_find_or_create() returning %d\n", 737 status); 738 return status; 739 740 out_err: 741 *target_out = NULL; 742 goto out; 743 } 744 745 746 /* public name cache interface, declared in name_cache.h */ 747 748 /* assuming no hard links, calculate how many entries will fit in the cache */ 749 #define SIZE_PER_ENTRY (ATTR_ENTRY_SIZE + NAME_ENTRY_SIZE) 750 #define NAME_CACHE_MAX_ENTRIES (NAME_CACHE_MAX_SIZE / SIZE_PER_ENTRY) 751 752 int nfs41_name_cache_create( 753 OUT struct nfs41_name_cache **cache_out) 754 { 755 struct nfs41_name_cache *cache; 756 int status = NO_ERROR; 757 758 dprintf(NCLVL1, "nfs41_name_cache_create()\n"); 759 760 /* allocate the cache */ 761 cache = calloc(1, sizeof(struct nfs41_name_cache)); 762 if (cache == NULL) { 763 status = GetLastError(); 764 goto out; 765 } 766 767 list_init(&cache->exp_entries); 768 cache->expiration = NAME_CACHE_EXPIRATION; 769 cache->max_entries = NAME_CACHE_MAX_ENTRIES; 770 cache->max_delegations = NAME_CACHE_MAX_ENTRIES / 2; 771 InitializeSRWLock(&cache->lock); 772 773 /* allocate a pool of entries */ 774 cache->pool = calloc(cache->max_entries, NAME_ENTRY_SIZE); 775 if (cache->pool == NULL) { 776 status = GetLastError(); 777 goto out_err_cache; 778 } 779 780 /* initialize the attribute cache */ 781 status = attr_cache_init(&cache->attributes, cache->max_entries); 782 if (status) 783 goto out_err_pool; 784 785 *cache_out = cache; 786 out: 787 return status; 788 789 out_err_pool: 790 free(cache->pool); 791 out_err_cache: 792 free(cache); 793 goto out; 794 } 795 796 int nfs41_name_cache_free( 797 IN struct nfs41_name_cache **cache_out) 798 { 799 struct nfs41_name_cache *cache = *cache_out; 800 int status = NO_ERROR; 801 802 dprintf(NCLVL1, "nfs41_name_cache_free()\n"); 803 804 /* free the attribute cache */ 805 attr_cache_free(&cache->attributes); 806 807 /* free the name entry pool */ 808 free(cache->pool); 809 free(cache); 810 *cache_out = NULL; 811 return status; 812 } 813 814 static __inline void copy_fh( 815 OUT nfs41_fh *dst, 816 IN OPTIONAL const struct name_cache_entry *src) 817 { 818 if (src) 819 fh_copy(dst, &src->fh); 820 else 821 dst->len = 0; 822 } 823 824 int nfs41_name_cache_lookup( 825 IN struct nfs41_name_cache *cache, 826 IN const char *path, 827 IN const char *path_end, 828 OUT OPTIONAL const char **remaining_path_out, 829 OUT OPTIONAL nfs41_fh *parent_out, 830 OUT OPTIONAL nfs41_fh *target_out, 831 OUT OPTIONAL nfs41_file_info *info_out, 832 OUT OPTIONAL bool_t *is_negative) 833 { 834 struct name_cache_entry *parent, *target; 835 const char *path_pos = path; 836 int status; 837 838 AcquireSRWLockShared(&cache->lock); 839 840 if (!name_cache_enabled(cache)) { 841 status = ERROR_NOT_SUPPORTED; 842 goto out_unlock; 843 } 844 845 status = name_cache_lookup(cache, 1, path, path_end, 846 &path_pos, &parent, &target, is_negative); 847 848 if (parent_out) copy_fh(parent_out, parent); 849 if (target_out) copy_fh(target_out, target); 850 if (info_out && target && target->attributes) 851 copy_attrs(info_out, target->attributes); 852 853 out_unlock: 854 ReleaseSRWLockShared(&cache->lock); 855 if (remaining_path_out) *remaining_path_out = path_pos; 856 return status; 857 } 858 859 int nfs41_attr_cache_lookup( 860 IN struct nfs41_name_cache *cache, 861 IN uint64_t fileid, 862 OUT nfs41_file_info *info_out) 863 { 864 struct attr_cache_entry *entry; 865 int status = NO_ERROR; 866 867 dprintf(NCLVL1, "--> nfs41_attr_cache_lookup(%llu)\n", fileid); 868 869 AcquireSRWLockShared(&cache->lock); 870 871 if (!name_cache_enabled(cache)) { 872 status = ERROR_NOT_SUPPORTED; 873 goto out_unlock; 874 } 875 876 entry = attr_cache_search(&cache->attributes, fileid); 877 if (entry == NULL || attr_cache_entry_expired(entry)) { 878 status = ERROR_FILE_NOT_FOUND; 879 goto out_unlock; 880 } 881 882 copy_attrs(info_out, entry); 883 884 out_unlock: 885 ReleaseSRWLockShared(&cache->lock); 886 887 dprintf(NCLVL1, "<-- nfs41_attr_cache_lookup() returning %d\n", status); 888 return status; 889 } 890 891 int nfs41_attr_cache_update( 892 IN struct nfs41_name_cache *cache, 893 IN uint64_t fileid, 894 IN const nfs41_file_info *info) 895 { 896 struct attr_cache_entry *entry; 897 int status = NO_ERROR; 898 899 dprintf(NCLVL1, "--> nfs41_attr_cache_update(%llu)\n", fileid); 900 901 AcquireSRWLockExclusive(&cache->lock); 902 903 if (!name_cache_enabled(cache)) { 904 status = ERROR_NOT_SUPPORTED; 905 goto out_unlock; 906 } 907 908 entry = attr_cache_search(&cache->attributes, fileid); 909 if (entry == NULL) { 910 status = ERROR_FILE_NOT_FOUND; 911 goto out_unlock; 912 } 913 914 attr_cache_update(entry, info, OPEN_DELEGATE_NONE); 915 916 out_unlock: 917 ReleaseSRWLockExclusive(&cache->lock); 918 919 dprintf(NCLVL1, "<-- nfs41_attr_cache_update() returning %d\n", status); 920 return status; 921 } 922 923 int nfs41_name_cache_insert( 924 IN struct nfs41_name_cache *cache, 925 IN const char *path, 926 IN const nfs41_component *name, 927 IN OPTIONAL const nfs41_fh *fh, 928 IN OPTIONAL const nfs41_file_info *info, 929 IN OPTIONAL const change_info4 *cinfo, 930 IN enum open_delegation_type4 delegation) 931 { 932 struct name_cache_entry *parent, *target; 933 int status; 934 935 dprintf(NCLVL1, "--> nfs41_name_cache_insert('%.*s')\n", 936 name->name + name->len - path, path); 937 938 AcquireSRWLockExclusive(&cache->lock); 939 940 if (!name_cache_enabled(cache)) { 941 status = ERROR_NOT_SUPPORTED; 942 goto out_unlock; 943 } 944 945 /* limit the number of delegations to prevent attr cache starvation */ 946 if (is_delegation(delegation) && 947 cache->delegations >= cache->max_delegations) { 948 status = ERROR_TOO_MANY_OPEN_FILES; 949 goto out_unlock; 950 } 951 952 /* an empty path or component implies the root entry */ 953 if (path == NULL || name == NULL || name->len == 0) { 954 /* create the root entry if it doesn't exist */ 955 if (cache->root == NULL) { 956 const nfs41_component name = { "ROOT", 4 }; 957 status = name_cache_entry_create(cache, &name, &cache->root); 958 if (status) 959 goto out_err_deleg; 960 } 961 target = cache->root; 962 } else { 963 /* find/create an entry under its parent */ 964 status = name_cache_lookup(cache, 0, path, 965 name->name, NULL, NULL, &parent, NULL); 966 if (status) 967 goto out_err_deleg; 968 969 if (cinfo && name_cache_entry_changed(cache, parent, cinfo)) { 970 name_cache_entry_invalidate(cache, parent); 971 goto out_err_deleg; 972 } 973 974 status = name_cache_find_or_create(cache, parent, name, &target); 975 if (status) 976 goto out_err_deleg; 977 } 978 979 /* pass in the new fh/attributes */ 980 status = name_cache_entry_update(cache, target, fh, info, delegation); 981 if (status) 982 goto out_err_update; 983 984 out_unlock: 985 ReleaseSRWLockExclusive(&cache->lock); 986 987 dprintf(NCLVL1, "<-- nfs41_name_cache_insert() returning %d\n", 988 status); 989 return status; 990 991 out_err_update: 992 /* a failure in name_cache_entry_update() leaves a negative entry 993 * where there shouldn't be one; remove it from the cache */ 994 name_cache_entry_invalidate(cache, target); 995 996 out_err_deleg: 997 if (is_delegation(delegation)) { 998 /* we still need a reference to the attributes for the delegation */ 999 struct attr_cache_entry *attributes; 1000 status = attr_cache_find_or_create(&cache->attributes, 1001 info->fileid, &attributes); 1002 if (status == NO_ERROR) { 1003 attr_cache_update(attributes, info, delegation); 1004 cache->delegations++; 1005 } 1006 else 1007 status = ERROR_TOO_MANY_OPEN_FILES; 1008 } 1009 goto out_unlock; 1010 } 1011 1012 int nfs41_name_cache_delegreturn( 1013 IN struct nfs41_name_cache *cache, 1014 IN uint64_t fileid, 1015 IN const char *path, 1016 IN const nfs41_component *name) 1017 { 1018 struct name_cache_entry *parent, *target; 1019 struct attr_cache_entry *attributes; 1020 int status; 1021 1022 dprintf(NCLVL1, "--> nfs41_name_cache_delegreturn(%llu, '%s')\n", 1023 fileid, path); 1024 1025 AcquireSRWLockExclusive(&cache->lock); 1026 1027 if (!name_cache_enabled(cache)) { 1028 status = ERROR_NOT_SUPPORTED; 1029 goto out_unlock; 1030 } 1031 1032 status = name_cache_lookup(cache, 0, path, 1033 name->name + name->len, NULL, &parent, &target, NULL); 1034 if (status == NO_ERROR) { 1035 /* put the name cache entry back on the exp_entries list */ 1036 list_add_head(&cache->exp_entries, &target->exp_entry); 1037 name_cache_entry_updated(cache, target); 1038 1039 attributes = target->attributes; 1040 } else { 1041 /* should still have an attr cache entry */ 1042 attributes = attr_cache_search(&cache->attributes, fileid); 1043 } 1044 1045 if (attributes == NULL) { 1046 status = ERROR_FILE_NOT_FOUND; 1047 goto out_unlock; 1048 } 1049 1050 /* release the reference from name_cache_entry_update() */ 1051 if (attributes->delegated) { 1052 attributes->delegated = FALSE; 1053 attr_cache_entry_deref(&cache->attributes, attributes); 1054 assert(cache->delegations > 0); 1055 cache->delegations--; 1056 } 1057 status = NO_ERROR; 1058 1059 out_unlock: 1060 ReleaseSRWLockExclusive(&cache->lock); 1061 1062 dprintf(NCLVL1, "<-- nfs41_name_cache_delegreturn() returning %d\n", status); 1063 return status; 1064 } 1065 1066 int nfs41_name_cache_remove( 1067 IN struct nfs41_name_cache *cache, 1068 IN const char *path, 1069 IN const nfs41_component *name, 1070 IN uint64_t fileid, 1071 IN const change_info4 *cinfo) 1072 { 1073 struct name_cache_entry *parent, *target; 1074 struct attr_cache_entry *attributes = NULL; 1075 int status; 1076 1077 dprintf(NCLVL1, "--> nfs41_name_cache_remove('%s')\n", path); 1078 1079 AcquireSRWLockExclusive(&cache->lock); 1080 1081 if (!name_cache_enabled(cache)) { 1082 status = ERROR_NOT_SUPPORTED; 1083 goto out_unlock; 1084 } 1085 1086 status = name_cache_lookup(cache, 0, path, 1087 name->name + name->len, NULL, &parent, &target, NULL); 1088 if (status == ERROR_PATH_NOT_FOUND) 1089 goto out_attributes; 1090 1091 if (cinfo && name_cache_entry_changed(cache, parent, cinfo)) { 1092 name_cache_entry_invalidate(cache, parent); 1093 goto out_attributes; 1094 } 1095 1096 if (status == ERROR_FILE_NOT_FOUND) 1097 goto out_attributes; 1098 1099 if (target->attributes) 1100 target->attributes->numlinks--; 1101 1102 /* make this a negative entry and unlink children */ 1103 name_cache_entry_update(cache, target, NULL, NULL, OPEN_DELEGATE_NONE); 1104 name_cache_unlink_children_recursive(cache, target); 1105 1106 out_unlock: 1107 ReleaseSRWLockExclusive(&cache->lock); 1108 1109 dprintf(NCLVL1, "<-- nfs41_name_cache_remove() returning %d\n", status); 1110 return status; 1111 1112 out_attributes: 1113 /* in the presence of other links, we need to update numlinks 1114 * regardless of a failure to find the target entry */ 1115 dprintf(NCLVL1, "nfs41_name_cache_remove: need to find attributes for %s\n", path); 1116 attributes = attr_cache_search(&cache->attributes, fileid); 1117 if (attributes) 1118 attributes->numlinks--; 1119 goto out_unlock; 1120 } 1121 1122 int nfs41_name_cache_rename( 1123 IN struct nfs41_name_cache *cache, 1124 IN const char *src_path, 1125 IN const nfs41_component *src_name, 1126 IN const change_info4 *src_cinfo, 1127 IN const char *dst_path, 1128 IN const nfs41_component *dst_name, 1129 IN const change_info4 *dst_cinfo) 1130 { 1131 struct name_cache_entry *src_parent, *src; 1132 struct name_cache_entry *dst_parent; 1133 int status = NO_ERROR; 1134 1135 dprintf(NCLVL1, "--> nfs41_name_cache_rename('%s' to '%s')\n", 1136 src_path, dst_path); 1137 1138 AcquireSRWLockExclusive(&cache->lock); 1139 1140 if (!name_cache_enabled(cache)) { 1141 status = ERROR_NOT_SUPPORTED; 1142 goto out_unlock; 1143 } 1144 1145 /* look up dst_parent */ 1146 status = name_cache_lookup(cache, 0, dst_path, 1147 dst_name->name, NULL, NULL, &dst_parent, NULL); 1148 /* we can't create the dst entry without a parent */ 1149 if (status || dst_parent->attributes == NULL) { 1150 /* if src exists, make it negative */ 1151 dprintf(NCLVL1, "nfs41_name_cache_rename: adding negative cache " 1152 "entry for %.*s\n", src_name->len, src_name->name); 1153 status = name_cache_lookup(cache, 0, src_path, 1154 src_name->name + src_name->len, NULL, NULL, &src, NULL); 1155 if (status == NO_ERROR) { 1156 name_cache_entry_update(cache, src, NULL, NULL, OPEN_DELEGATE_NONE); 1157 name_cache_unlink_children_recursive(cache, src); 1158 } 1159 status = ERROR_PATH_NOT_FOUND; 1160 goto out_unlock; 1161 } 1162 1163 /* look up src_parent and src */ 1164 status = name_cache_lookup(cache, 0, src_path, 1165 src_name->name + src_name->len, NULL, &src_parent, &src, NULL); 1166 /* we can't create the dst entry without valid attributes */ 1167 if (status || src->attributes == NULL) { 1168 /* remove dst if it exists */ 1169 struct name_cache_entry *dst; 1170 dprintf(NCLVL1, "nfs41_name_cache_rename: removing negative cache " 1171 "entry for %.*s\n", dst_name->len, dst_name->name); 1172 dst = name_cache_search(cache, dst_parent, dst_name); 1173 if (dst) name_cache_unlink(cache, dst); 1174 goto out_unlock; 1175 } 1176 1177 if (name_cache_entry_changed(cache, dst_parent, dst_cinfo)) { 1178 name_cache_entry_invalidate(cache, dst_parent); 1179 /* if dst_parent and src_parent are both gone, 1180 * we no longer have an entry to rename */ 1181 if (dst_parent == src_parent) 1182 goto out_unlock; 1183 } else { 1184 struct name_cache_entry *existing; 1185 existing = name_cache_search(cache, dst_parent, dst_name); 1186 if (existing) { 1187 if (existing == src) 1188 goto out_unlock; 1189 /* remove the existing entry, but don't unlink it yet; 1190 * we may reuse it for a negative entry */ 1191 name_cache_remove(existing, dst_parent); 1192 } 1193 1194 /* move the src entry under dst_parent */ 1195 name_cache_remove(src, src_parent); 1196 name_cache_entry_rename(src, dst_name); 1197 name_cache_insert(src, dst_parent); 1198 1199 if (existing) { 1200 /* recycle 'existing' as the negative entry 'src' */ 1201 name_cache_entry_rename(existing, src_name); 1202 name_cache_insert(existing, src_parent); 1203 } 1204 src = existing; 1205 } 1206 1207 if (name_cache_entry_changed(cache, src_parent, src_cinfo)) { 1208 name_cache_entry_invalidate(cache, src_parent); 1209 goto out_unlock; 1210 } 1211 1212 /* leave a negative entry where the file used to be */ 1213 if (src == NULL) { 1214 /* src was moved, create a new entry in its place */ 1215 status = name_cache_find_or_create(cache, src_parent, src_name, &src); 1216 if (status) 1217 goto out_unlock; 1218 } 1219 name_cache_entry_update(cache, src, NULL, NULL, OPEN_DELEGATE_NONE); 1220 name_cache_unlink_children_recursive(cache, src); 1221 1222 out_unlock: 1223 ReleaseSRWLockExclusive(&cache->lock); 1224 1225 dprintf(NCLVL1, "<-- nfs41_name_cache_rename() returning %d\n", status); 1226 return status; 1227 } 1228 1229 /* nfs41_name_cache_resolve_fh() */ 1230 1231 #define MAX_PUTFH_PER_COMPOUND 16 1232 1233 static bool_t get_path_fhs( 1234 IN struct nfs41_name_cache *cache, 1235 IN nfs41_abs_path *path, 1236 IN OUT const char **path_pos, 1237 IN uint32_t max_components, 1238 OUT nfs41_path_fh *files, 1239 OUT uint32_t *count) 1240 { 1241 struct name_cache_entry *target; 1242 const char *path_end = path->path + path->len; 1243 nfs41_component *name; 1244 uint32_t i; 1245 int status; 1246 1247 *count = 0; 1248 1249 AcquireSRWLockShared(&cache->lock); 1250 1251 /* look up the parent of the first component */ 1252 status = name_cache_lookup(cache, 1, path->path, 1253 *path_pos, NULL, NULL, &target, NULL); 1254 if (status) 1255 goto out_unlock; 1256 1257 for (i = 0; i < max_components; i++) { 1258 files[i].path = path; 1259 name = &files[i].name; 1260 1261 if (!next_component(*path_pos, path_end, name)) 1262 break; 1263 *path_pos = name->name + name->len; 1264 1265 target = name_cache_search(cache, target, name); 1266 if (target == NULL || entry_invis(target, NULL)) { 1267 if (is_last_component(name->name, path_end)) 1268 status = ERROR_FILE_NOT_FOUND; 1269 else 1270 status = ERROR_PATH_NOT_FOUND; 1271 goto out_unlock; 1272 } 1273 /* make copies for use outside of cache->lock */ 1274 fh_copy(&files[i].fh, &target->fh); 1275 (*count)++; 1276 } 1277 1278 out_unlock: 1279 ReleaseSRWLockShared(&cache->lock); 1280 return *count && status == 0; 1281 } 1282 1283 static int rpc_array_putfh( 1284 IN nfs41_session *session, 1285 IN nfs41_path_fh *files, 1286 IN uint32_t count, 1287 OUT uint32_t *valid_out) 1288 { 1289 nfs41_compound compound; 1290 nfs_argop4 argops[1+MAX_PUTFH_PER_COMPOUND]; 1291 nfs_resop4 resops[1+MAX_PUTFH_PER_COMPOUND]; 1292 nfs41_sequence_args sequence_args; 1293 nfs41_sequence_res sequence_res = { 0 }; 1294 nfs41_putfh_args putfh_args[MAX_PUTFH_PER_COMPOUND]; 1295 nfs41_putfh_res putfh_res[MAX_PUTFH_PER_COMPOUND] = { 0 }; 1296 uint32_t i; 1297 int status; 1298 1299 *valid_out = 0; 1300 1301 compound_init(&compound, argops, resops, "array_putfh"); 1302 1303 compound_add_op(&compound, OP_SEQUENCE, &sequence_args, &sequence_res); 1304 nfs41_session_sequence(&sequence_args, session, 0); 1305 1306 for (i = 0; i < count; i++){ 1307 compound_add_op(&compound, OP_PUTFH, &putfh_args[i], &putfh_res[i]); 1308 putfh_args[i].file = &files[i]; 1309 putfh_args[i].in_recovery = 1; 1310 } 1311 1312 status = compound_encode_send_decode(session, &compound, TRUE); 1313 if (status) goto out; 1314 1315 status = sequence_res.sr_status; 1316 if (status) goto out; 1317 1318 for (i = 0; i < count; i++) { 1319 status = putfh_res[i].status; 1320 if (status) break; 1321 } 1322 *valid_out = i; 1323 out: 1324 return status; 1325 } 1326 1327 static int delete_stale_component( 1328 IN struct nfs41_name_cache *cache, 1329 IN nfs41_session *session, 1330 IN const nfs41_abs_path *path, 1331 IN const nfs41_component *component) 1332 { 1333 struct name_cache_entry *target; 1334 int status; 1335 1336 dprintf(NCLVL1, "--> delete_stale_component('%s')\n", 1337 component->name); 1338 1339 AcquireSRWLockExclusive(&cache->lock); 1340 1341 status = name_cache_lookup(cache, 0, path->path, 1342 component->name + component->len, NULL, NULL, &target, NULL); 1343 if (status == NO_ERROR) 1344 name_cache_unlink(cache, target); 1345 1346 ReleaseSRWLockExclusive(&cache->lock); 1347 1348 dprintf(NCLVL1, "<-- delete_stale_component() returning %d\n", status); 1349 return status; 1350 } 1351 1352 static __inline uint32_t max_putfh_components( 1353 IN const nfs41_session *session) 1354 { 1355 const uint32_t comps = session->fore_chan_attrs.ca_maxoperations - 1; 1356 return min(comps, MAX_PUTFH_PER_COMPOUND); 1357 } 1358 1359 int nfs41_name_cache_remove_stale( 1360 IN struct nfs41_name_cache *cache, 1361 IN nfs41_session *session, 1362 IN nfs41_abs_path *path) 1363 { 1364 nfs41_path_fh files[MAX_PUTFH_PER_COMPOUND]; 1365 const char *path_pos = path->path; 1366 const char* const path_end = path->path + path->len; 1367 const uint32_t max_components = max_putfh_components(session); 1368 uint32_t count, index; 1369 int status = NO_ERROR; 1370 1371 AcquireSRWLockShared(&cache->lock); 1372 1373 /* if there's no cache, don't check any components */ 1374 if (!name_cache_enabled(cache)) 1375 path_pos = path_end; 1376 1377 ReleaseSRWLockShared(&cache->lock); 1378 1379 /* hold a lock on the path to protect against rename */ 1380 AcquireSRWLockShared(&path->lock); 1381 1382 while (get_path_fhs(cache, path, &path_pos, max_components, files, &count)) { 1383 status = rpc_array_putfh(session, files, count, &index); 1384 1385 if (status == NFS4ERR_STALE || status == NFS4ERR_FHEXPIRED) { 1386 status = delete_stale_component(cache, 1387 session, path, &files[index].name); 1388 break; 1389 } 1390 if (status) { 1391 status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND); 1392 break; 1393 } 1394 } 1395 1396 ReleaseSRWLockShared(&path->lock); 1397 1398 return status; 1399 } 1400