1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
27 #include "nfs41_ops.h"
28 #include "nfs41_compound.h"
29 #include "name_cache.h"
32 #include "daemon_debug.h"
35 /* dprintf levels for name cache logging */
42 #define NAME_CACHE_EXPIRATION 20 /* TODO: get from configuration */
44 /* allow up to 256K of memory for name and attribute cache entries */
45 #define NAME_CACHE_MAX_SIZE 262144
47 /* negative lookup caching
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 */
55 /* delegations and cache feedback
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.
72 static __inline bool_t
is_delegation(
73 IN
enum open_delegation_type4 type
)
75 return type
== OPEN_DELEGATE_READ
|| type
== OPEN_DELEGATE_WRITE
;
80 struct attr_cache_entry
{
81 RB_ENTRY(attr_cache_entry
) rbnode
;
82 struct list_entry free_entry
;
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
;
98 unsigned ref_count
: 26;
100 unsigned invalidated
: 1;
101 unsigned delegated
: 1;
103 #define ATTR_ENTRY_SIZE sizeof(struct attr_cache_entry)
105 RB_HEAD(attr_tree
, attr_cache_entry
);
108 struct attr_tree head
;
109 struct attr_cache_entry
*pool
;
110 struct list_entry free_entries
;
113 int attr_cmp(struct attr_cache_entry
*lhs
, struct attr_cache_entry
*rhs
)
115 return lhs
->fileid
< rhs
->fileid
? -1 : lhs
->fileid
> rhs
->fileid
;
117 RB_GENERATE(attr_tree
, attr_cache_entry
, rbnode
, attr_cmp
)
120 /* attr_cache_entry */
121 #define attr_entry(pos) list_container(pos, struct attr_cache_entry, free_entry)
123 static int attr_cache_entry_create(
124 IN
struct attr_cache
*cache
,
126 OUT
struct attr_cache_entry
**entry_out
)
128 struct attr_cache_entry
*entry
;
129 int status
= NO_ERROR
;
131 /* get the next entry from free_entries and remove it */
132 if (list_empty(&cache
->free_entries
)) {
133 status
= ERROR_OUTOFMEMORY
;
136 entry
= attr_entry(cache
->free_entries
.next
);
137 list_remove(&entry
->free_entry
);
139 entry
->fileid
= fileid
;
140 entry
->invalidated
= FALSE
;
141 entry
->delegated
= FALSE
;
147 static __inline
void attr_cache_entry_free(
148 IN
struct attr_cache
*cache
,
149 IN
struct attr_cache_entry
*entry
)
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
);
157 static __inline
void attr_cache_entry_ref(
158 IN
struct attr_cache
*cache
,
159 IN
struct attr_cache_entry
*entry
)
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
);
166 static __inline
void attr_cache_entry_deref(
167 IN
struct attr_cache
*cache
,
168 IN
struct attr_cache_entry
*entry
)
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
);
174 if (entry
->ref_count
== 0)
175 attr_cache_entry_free(cache
, entry
);
178 static __inline
int attr_cache_entry_expired(
179 IN
const struct attr_cache_entry
*entry
)
181 return entry
->invalidated
||
182 (!entry
->delegated
&& time(NULL
) > entry
->expiration
);
186 static int attr_cache_init(
187 IN
struct attr_cache
*cache
,
188 IN
uint32_t max_entries
)
191 int status
= NO_ERROR
;
193 /* allocate a pool of entries */
194 cache
->pool
= calloc(max_entries
, ATTR_ENTRY_SIZE
);
195 if (cache
->pool
== NULL
) {
196 status
= GetLastError();
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
);
210 static void attr_cache_free(
211 IN
struct attr_cache
*cache
)
216 list_init(&cache
->free_entries
);
219 static struct attr_cache_entry
* attr_cache_search(
220 IN
struct attr_cache
*cache
,
223 /* find an entry that matches fileid */
224 struct attr_cache_entry tmp
;
226 return RB_FIND(attr_tree
, &cache
->head
, &tmp
);
229 static int attr_cache_insert(
230 IN
struct attr_cache
*cache
,
231 IN
struct attr_cache_entry
*entry
)
233 int status
= NO_ERROR
;
235 dprintf(NCLVL2
, "--> attr_cache_insert(%llu)\n", entry
->fileid
);
237 if (RB_INSERT(attr_tree
, &cache
->head
, entry
))
238 status
= ERROR_FILE_EXISTS
;
240 dprintf(NCLVL2
, "<-- attr_cache_insert() returning %d\n", status
);
244 static int attr_cache_find_or_create(
245 IN
struct attr_cache
*cache
,
247 OUT
struct attr_cache_entry
**entry_out
)
249 struct attr_cache_entry
*entry
;
250 int status
= NO_ERROR
;
252 dprintf(NCLVL1
, "--> attr_cache_find_or_create(%llu)\n", fileid
);
254 /* look for an existing entry */
255 entry
= attr_cache_search(cache
, fileid
);
257 /* create and insert */
258 status
= attr_cache_entry_create(cache
, fileid
, &entry
);
262 status
= attr_cache_insert(cache
, entry
);
267 /* take a reference on success */
268 attr_cache_entry_ref(cache
, entry
);
272 dprintf(NCLVL1
, "<-- attr_cache_find_or_create() returning %d\n",
277 attr_cache_entry_free(cache
, entry
);
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
)
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
;
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
;
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
;
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
;
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
;
321 if (info
->attrmask
.arr
[1] & FATTR4_WORD1_SYSTEM
)
322 entry
->system
= info
->system
;
325 if (is_delegation(delegation
))
326 entry
->delegated
= TRUE
;
329 static void copy_attrs(
330 OUT nfs41_file_info
*dst
,
331 IN
const struct attr_cache_entry
*src
)
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
;
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
;
361 RB_HEAD(name_tree
, name_cache_entry
);
362 struct name_cache_entry
{
363 char component
[NFS41_MAX_COMPONENT_LEN
];
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
;
371 unsigned short component_len
;
373 #define NAME_ENTRY_SIZE sizeof(struct name_cache_entry)
375 int name_cmp(struct name_cache_entry
*lhs
, struct name_cache_entry
*rhs
)
377 const int diff
= rhs
->component_len
- lhs
->component_len
;
378 return diff
? diff
: strncmp(lhs
->component
, rhs
->component
, lhs
->component_len
);
380 RB_GENERATE(name_tree
, name_cache_entry
, rbnode
, name_cmp
)
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 */
389 uint32_t max_entries
;
390 uint32_t delegations
;
391 uint32_t max_delegations
;
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 */
399 #define name_entry(pos) list_container(pos, struct name_cache_entry, exp_entry)
401 static __inline bool_t
name_cache_enabled(
402 IN
struct nfs41_name_cache
*cache
)
404 return cache
->expiration
> 0;
407 static __inline
void name_cache_entry_rename(
408 OUT
struct name_cache_entry
*entry
,
409 IN
const nfs41_component
*component
)
411 StringCchCopyNA(entry
->component
, NFS41_MAX_COMPONENT_LEN
,
412 component
->name
, component
->len
);
413 entry
->component_len
= component
->len
;
416 static __inline
void name_cache_remove(
417 IN
struct name_cache_entry
*entry
,
418 IN
struct name_cache_entry
*parent
)
420 RB_REMOVE(name_tree
, &parent
->rbchildren
, entry
);
421 entry
->parent
= NULL
;
424 static void name_cache_unlink_children_recursive(
425 IN
struct nfs41_name_cache
*cache
,
426 IN
struct name_cache_entry
*parent
);
428 static __inline
void name_cache_unlink(
429 IN
struct nfs41_name_cache
*cache
,
430 IN
struct name_cache_entry
*entry
)
432 /* remove the entry from the tree */
434 name_cache_remove(entry
, entry
->parent
);
435 else if (entry
== cache
->root
)
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
;
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
);
450 static void name_cache_unlink_children_recursive(
451 IN
struct nfs41_name_cache
*cache
,
452 IN
struct name_cache_entry
*parent
)
454 struct name_cache_entry
*entry
, *tmp
;
455 RB_FOREACH_SAFE(entry
, name_tree
, &parent
->rbchildren
, tmp
)
456 name_cache_unlink(cache
, entry
);
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
)
464 int status
= NO_ERROR
;
465 struct name_cache_entry
*entry
;
467 if (cache
->entries
>= cache
->max_entries
) {
468 /* scavenge the oldest entry */
469 if (list_empty(&cache
->exp_entries
)) {
470 status
= ERROR_OUTOFMEMORY
;
473 entry
= name_entry(cache
->exp_entries
.prev
);
474 name_cache_unlink(cache
, entry
);
476 dprintf(NCLVL2
, "name_cache_entry_create('%s') scavenged 0x%p\n",
477 component
->name
, entry
);
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
);
485 name_cache_entry_rename(entry
, component
);
492 static void name_cache_entry_accessed(
493 IN
struct nfs41_name_cache
*cache
,
494 IN
struct name_cache_entry
*entry
)
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 */
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
);
504 if (entry
== entry
->parent
)
506 entry
= entry
->parent
;
510 static void name_cache_entry_updated(
511 IN
struct nfs41_name_cache
*cache
,
512 IN
struct name_cache_entry
*entry
)
514 /* update the expiration timer */
515 entry
->expiration
= time(NULL
) + cache
->expiration
;
516 name_cache_entry_accessed(cache
, entry
);
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
)
526 int status
= NO_ERROR
;
529 fh_copy(&entry
->fh
, fh
);
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
);
542 attr_cache_update(entry
->attributes
, info
, delegation
);
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
++;
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
;
558 name_cache_entry_updated(cache
, entry
);
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
)
568 if (entry
->attributes
== NULL
)
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
);
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
);
587 static void name_cache_entry_invalidate(
588 IN
struct nfs41_name_cache
*cache
,
589 IN
struct name_cache_entry
*entry
)
591 dprintf(NCLVL1
, "name_cache_entry_invalidate('%s')\n", entry
->component
);
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;
598 name_cache_unlink(cache
, entry
);
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
)
606 struct name_cache_entry tmp
, *entry
;
608 dprintf(NCLVL2
, "--> name_cache_search('%.*s' under '%s')\n",
609 component
->len
, component
->name
, parent
->component
);
611 StringCchCopyNA(tmp
.component
, NFS41_MAX_COMPONENT_LEN
,
612 component
->name
, component
->len
);
613 tmp
.component_len
= component
->len
;
615 entry
= RB_FIND(name_tree
, &parent
->rbchildren
, &tmp
);
617 dprintf(NCLVL2
, "<-- name_cache_search() "
618 "found existing entry 0x%p\n", entry
);
620 dprintf(NCLVL2
, "<-- name_cache_search() returning NULL\n");
624 static int entry_invis(
625 IN
struct name_cache_entry
*entry
,
626 OUT OPTIONAL bool_t
*is_negative
)
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
);
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
);
639 /* attribute entry expired? */
640 if (attr_cache_entry_expired(entry
->attributes
)) {
641 dprintf(NCLVL2
, "attr_entry_expired(%llu)\n",
642 entry
->attributes
->fileid
);
648 static int name_cache_lookup(
649 IN
struct nfs41_name_cache
*cache
,
650 IN bool_t skip_invis
,
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
)
658 struct name_cache_entry
*parent
, *target
;
659 nfs41_component component
;
660 const char *path_pos
;
661 int status
= NO_ERROR
;
663 dprintf(NCLVL1
, "--> name_cache_lookup('%s')\n", path
);
666 target
= cache
->root
;
667 component
.name
= path_pos
= path
;
669 if (target
== NULL
|| (skip_invis
&& entry_invis(target
, is_negative
))) {
671 status
= ERROR_PATH_NOT_FOUND
;
675 while (next_component(path_pos
, path_end
, &component
)) {
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
))) {
681 if (is_last_component(component
.name
, path_end
))
682 status
= ERROR_FILE_NOT_FOUND
;
684 status
= ERROR_PATH_NOT_FOUND
;
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
);
696 static int name_cache_insert(
697 IN
struct name_cache_entry
*entry
,
698 IN
struct name_cache_entry
*parent
)
700 int status
= NO_ERROR
;
702 dprintf(NCLVL2
, "--> name_cache_insert('%s')\n", entry
->component
);
704 if (RB_INSERT(name_tree
, &parent
->rbchildren
, entry
))
705 status
= ERROR_FILE_EXISTS
;
706 entry
->parent
= parent
;
708 dprintf(NCLVL2
, "<-- name_cache_insert() returning %u\n", status
);
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
)
718 int status
= NO_ERROR
;
720 dprintf(NCLVL1
, "--> name_cache_find_or_create('%.*s' under '%s')\n",
721 component
->len
, component
->name
, parent
->component
);
723 *target_out
= name_cache_search(cache
, parent
, component
);
727 status
= name_cache_entry_create(cache
, component
, target_out
);
731 status
= name_cache_insert(*target_out
, parent
);
736 dprintf(NCLVL1
, "<-- name_cache_find_or_create() returning %d\n",
746 /* public name cache interface, declared in name_cache.h */
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)
752 int nfs41_name_cache_create(
753 OUT
struct nfs41_name_cache
**cache_out
)
755 struct nfs41_name_cache
*cache
;
756 int status
= NO_ERROR
;
758 dprintf(NCLVL1
, "nfs41_name_cache_create()\n");
760 /* allocate the cache */
761 cache
= calloc(1, sizeof(struct nfs41_name_cache
));
763 status
= GetLastError();
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
);
773 /* allocate a pool of entries */
774 cache
->pool
= calloc(cache
->max_entries
, NAME_ENTRY_SIZE
);
775 if (cache
->pool
== NULL
) {
776 status
= GetLastError();
780 /* initialize the attribute cache */
781 status
= attr_cache_init(&cache
->attributes
, cache
->max_entries
);
796 int nfs41_name_cache_free(
797 IN
struct nfs41_name_cache
**cache_out
)
799 struct nfs41_name_cache
*cache
= *cache_out
;
800 int status
= NO_ERROR
;
802 dprintf(NCLVL1
, "nfs41_name_cache_free()\n");
804 /* free the attribute cache */
805 attr_cache_free(&cache
->attributes
);
807 /* free the name entry pool */
814 static __inline
void copy_fh(
816 IN OPTIONAL
const struct name_cache_entry
*src
)
819 fh_copy(dst
, &src
->fh
);
824 int nfs41_name_cache_lookup(
825 IN
struct nfs41_name_cache
*cache
,
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
)
834 struct name_cache_entry
*parent
, *target
;
835 const char *path_pos
= path
;
838 AcquireSRWLockShared(&cache
->lock
);
840 if (!name_cache_enabled(cache
)) {
841 status
= ERROR_NOT_SUPPORTED
;
845 status
= name_cache_lookup(cache
, 1, path
, path_end
,
846 &path_pos
, &parent
, &target
, is_negative
);
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
);
854 ReleaseSRWLockShared(&cache
->lock
);
855 if (remaining_path_out
) *remaining_path_out
= path_pos
;
859 int nfs41_attr_cache_lookup(
860 IN
struct nfs41_name_cache
*cache
,
862 OUT nfs41_file_info
*info_out
)
864 struct attr_cache_entry
*entry
;
865 int status
= NO_ERROR
;
867 dprintf(NCLVL1
, "--> nfs41_attr_cache_lookup(%llu)\n", fileid
);
869 AcquireSRWLockShared(&cache
->lock
);
871 if (!name_cache_enabled(cache
)) {
872 status
= ERROR_NOT_SUPPORTED
;
876 entry
= attr_cache_search(&cache
->attributes
, fileid
);
877 if (entry
== NULL
|| attr_cache_entry_expired(entry
)) {
878 status
= ERROR_FILE_NOT_FOUND
;
882 copy_attrs(info_out
, entry
);
885 ReleaseSRWLockShared(&cache
->lock
);
887 dprintf(NCLVL1
, "<-- nfs41_attr_cache_lookup() returning %d\n", status
);
891 int nfs41_attr_cache_update(
892 IN
struct nfs41_name_cache
*cache
,
894 IN
const nfs41_file_info
*info
)
896 struct attr_cache_entry
*entry
;
897 int status
= NO_ERROR
;
899 dprintf(NCLVL1
, "--> nfs41_attr_cache_update(%llu)\n", fileid
);
901 AcquireSRWLockExclusive(&cache
->lock
);
903 if (!name_cache_enabled(cache
)) {
904 status
= ERROR_NOT_SUPPORTED
;
908 entry
= attr_cache_search(&cache
->attributes
, fileid
);
910 status
= ERROR_FILE_NOT_FOUND
;
914 attr_cache_update(entry
, info
, OPEN_DELEGATE_NONE
);
917 ReleaseSRWLockExclusive(&cache
->lock
);
919 dprintf(NCLVL1
, "<-- nfs41_attr_cache_update() returning %d\n", status
);
923 int nfs41_name_cache_insert(
924 IN
struct nfs41_name_cache
*cache
,
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
)
932 struct name_cache_entry
*parent
, *target
;
935 dprintf(NCLVL1
, "--> nfs41_name_cache_insert('%.*s')\n",
936 name
->name
+ name
->len
- path
, path
);
938 AcquireSRWLockExclusive(&cache
->lock
);
940 if (!name_cache_enabled(cache
)) {
941 status
= ERROR_NOT_SUPPORTED
;
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
;
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
);
961 target
= cache
->root
;
963 /* find/create an entry under its parent */
964 status
= name_cache_lookup(cache
, 0, path
,
965 name
->name
, NULL
, NULL
, &parent
, NULL
);
969 if (cinfo
&& name_cache_entry_changed(cache
, parent
, cinfo
)) {
970 name_cache_entry_invalidate(cache
, parent
);
974 status
= name_cache_find_or_create(cache
, parent
, name
, &target
);
979 /* pass in the new fh/attributes */
980 status
= name_cache_entry_update(cache
, target
, fh
, info
, delegation
);
985 ReleaseSRWLockExclusive(&cache
->lock
);
987 dprintf(NCLVL1
, "<-- nfs41_name_cache_insert() returning %d\n",
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
);
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
++;
1007 status
= ERROR_TOO_MANY_OPEN_FILES
;
1012 int nfs41_name_cache_delegreturn(
1013 IN
struct nfs41_name_cache
*cache
,
1015 IN
const char *path
,
1016 IN
const nfs41_component
*name
)
1018 struct name_cache_entry
*parent
, *target
;
1019 struct attr_cache_entry
*attributes
;
1022 dprintf(NCLVL1
, "--> nfs41_name_cache_delegreturn(%llu, '%s')\n",
1025 AcquireSRWLockExclusive(&cache
->lock
);
1027 if (!name_cache_enabled(cache
)) {
1028 status
= ERROR_NOT_SUPPORTED
;
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
);
1039 attributes
= target
->attributes
;
1041 /* should still have an attr cache entry */
1042 attributes
= attr_cache_search(&cache
->attributes
, fileid
);
1045 if (attributes
== NULL
) {
1046 status
= ERROR_FILE_NOT_FOUND
;
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
--;
1060 ReleaseSRWLockExclusive(&cache
->lock
);
1062 dprintf(NCLVL1
, "<-- nfs41_name_cache_delegreturn() returning %d\n", status
);
1066 int nfs41_name_cache_remove(
1067 IN
struct nfs41_name_cache
*cache
,
1068 IN
const char *path
,
1069 IN
const nfs41_component
*name
,
1071 IN
const change_info4
*cinfo
)
1073 struct name_cache_entry
*parent
, *target
;
1074 struct attr_cache_entry
*attributes
= NULL
;
1077 dprintf(NCLVL1
, "--> nfs41_name_cache_remove('%s')\n", path
);
1079 AcquireSRWLockExclusive(&cache
->lock
);
1081 if (!name_cache_enabled(cache
)) {
1082 status
= ERROR_NOT_SUPPORTED
;
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
;
1091 if (cinfo
&& name_cache_entry_changed(cache
, parent
, cinfo
)) {
1092 name_cache_entry_invalidate(cache
, parent
);
1093 goto out_attributes
;
1096 if (status
== ERROR_FILE_NOT_FOUND
)
1097 goto out_attributes
;
1099 if (target
->attributes
)
1100 target
->attributes
->numlinks
--;
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
);
1107 ReleaseSRWLockExclusive(&cache
->lock
);
1109 dprintf(NCLVL1
, "<-- nfs41_name_cache_remove() returning %d\n", status
);
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
);
1118 attributes
->numlinks
--;
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
)
1131 struct name_cache_entry
*src_parent
, *src
;
1132 struct name_cache_entry
*dst_parent
;
1133 int status
= NO_ERROR
;
1135 dprintf(NCLVL1
, "--> nfs41_name_cache_rename('%s' to '%s')\n",
1136 src_path
, dst_path
);
1138 AcquireSRWLockExclusive(&cache
->lock
);
1140 if (!name_cache_enabled(cache
)) {
1141 status
= ERROR_NOT_SUPPORTED
;
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
);
1159 status
= ERROR_PATH_NOT_FOUND
;
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
);
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
)
1184 struct name_cache_entry
*existing
;
1185 existing
= name_cache_search(cache
, dst_parent
, dst_name
);
1187 if (existing
== src
)
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
);
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
);
1200 /* recycle 'existing' as the negative entry 'src' */
1201 name_cache_entry_rename(existing
, src_name
);
1202 name_cache_insert(existing
, src_parent
);
1207 if (name_cache_entry_changed(cache
, src_parent
, src_cinfo
)) {
1208 name_cache_entry_invalidate(cache
, src_parent
);
1212 /* leave a negative entry where the file used to be */
1214 /* src was moved, create a new entry in its place */
1215 status
= name_cache_find_or_create(cache
, src_parent
, src_name
, &src
);
1219 name_cache_entry_update(cache
, src
, NULL
, NULL
, OPEN_DELEGATE_NONE
);
1220 name_cache_unlink_children_recursive(cache
, src
);
1223 ReleaseSRWLockExclusive(&cache
->lock
);
1225 dprintf(NCLVL1
, "<-- nfs41_name_cache_rename() returning %d\n", status
);
1229 /* nfs41_name_cache_resolve_fh() */
1231 #define MAX_PUTFH_PER_COMPOUND 16
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
)
1241 struct name_cache_entry
*target
;
1242 const char *path_end
= path
->path
+ path
->len
;
1243 nfs41_component
*name
;
1249 AcquireSRWLockShared(&cache
->lock
);
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
);
1257 for (i
= 0; i
< max_components
; i
++) {
1258 files
[i
].path
= path
;
1259 name
= &files
[i
].name
;
1261 if (!next_component(*path_pos
, path_end
, name
))
1263 *path_pos
= name
->name
+ name
->len
;
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
;
1270 status
= ERROR_PATH_NOT_FOUND
;
1273 /* make copies for use outside of cache->lock */
1274 fh_copy(&files
[i
].fh
, &target
->fh
);
1279 ReleaseSRWLockShared(&cache
->lock
);
1280 return *count
&& status
== 0;
1283 static int rpc_array_putfh(
1284 IN nfs41_session
*session
,
1285 IN nfs41_path_fh
*files
,
1287 OUT
uint32_t *valid_out
)
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 };
1301 compound_init(&compound
, argops
, resops
, "array_putfh");
1303 compound_add_op(&compound
, OP_SEQUENCE
, &sequence_args
, &sequence_res
);
1304 nfs41_session_sequence(&sequence_args
, session
, 0);
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;
1312 status
= compound_encode_send_decode(session
, &compound
, TRUE
);
1313 if (status
) goto out
;
1315 status
= sequence_res
.sr_status
;
1316 if (status
) goto out
;
1318 for (i
= 0; i
< count
; i
++) {
1319 status
= putfh_res
[i
].status
;
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
)
1333 struct name_cache_entry
*target
;
1336 dprintf(NCLVL1
, "--> delete_stale_component('%s')\n",
1339 AcquireSRWLockExclusive(&cache
->lock
);
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
);
1346 ReleaseSRWLockExclusive(&cache
->lock
);
1348 dprintf(NCLVL1
, "<-- delete_stale_component() returning %d\n", status
);
1352 static __inline
uint32_t max_putfh_components(
1353 IN
const nfs41_session
*session
)
1355 const uint32_t comps
= session
->fore_chan_attrs
.ca_maxoperations
- 1;
1356 return min(comps
, MAX_PUTFH_PER_COMPOUND
);
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
)
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
;
1371 AcquireSRWLockShared(&cache
->lock
);
1373 /* if there's no cache, don't check any components */
1374 if (!name_cache_enabled(cache
))
1375 path_pos
= path_end
;
1377 ReleaseSRWLockShared(&cache
->lock
);
1379 /* hold a lock on the path to protect against rename */
1380 AcquireSRWLockShared(&path
->lock
);
1382 while (get_path_fhs(cache
, path
, &path_pos
, max_components
, files
, &count
)) {
1383 status
= rpc_array_putfh(session
, files
, count
, &index
);
1385 if (status
== NFS4ERR_STALE
|| status
== NFS4ERR_FHEXPIRED
) {
1386 status
= delete_stale_component(cache
,
1387 session
, path
, &files
[index
].name
);
1391 status
= nfs_to_windows_error(status
, ERROR_FILE_NOT_FOUND
);
1396 ReleaseSRWLockShared(&path
->lock
);