use a cell that actually exists
[arla.git] / arlad / fcache.c
blob4aac5ad111987fb548451a85eacd58f678f961ef
1 /*
2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 * This is the cache for files.
36 * The hash-table is keyed with (cell, volume, fid).
39 #include "arla_local.h"
40 RCSID("$Id$") ;
42 #include <nnpfs/nnpfs_queue.h>
44 #ifdef __CYGWIN32__
45 #include <windows.h>
46 #endif
49 * Prototypes
52 static int get_attr_bulk (FCacheEntry *parent_entry,
53 FCacheEntry *prefered_entry,
54 VenusFid *prefered_fid,
55 const char *prefered_name,
56 CredCacheEntry *ce);
58 static int
59 resolve_mp(FCacheEntry *e, VenusFid *ret_fid, CredCacheEntry **ce);
61 static int
62 fcache_want_bytes(uint64_t wanted);
64 static int
65 fcache_need_bytes(uint64_t needed);
67 static int
68 fcache_verify_data(FCacheEntry *e, CredCacheEntry *ce,
69 uint64_t offset, uint64_t end);
71 static void
72 setbusy_block(struct block *b, Bool val);
74 static void
75 throw_block(struct block *b, Bool usagep);
77 static int
78 fcache_update_usage(FCacheEntry *e, int nblocks);
81 * Local data for this module.
85 * Hash table for all the nodes known by the cache manager keyed by
86 * (cell, volume, vnode, unique).
89 static Hashtab *hashtab;
92 * LRU lists, sorted sorted in LRU-order. The head is the MRU and the
93 * tail the LRU, which is from where we get entries when we have no
94 * free ones left.
98 * List of all nodes known to kernel.
101 static List *kernel_node_lru;
104 * List of all nodes in hashtab that are not known to kernel.
107 static List *node_lru;
110 * Pool of all nodes not in `hashtab'.
113 static List *free_nodes;
116 * List of all data blocks known to kernel.
119 static List *kernel_block_lru;
122 * List of all data blocks not known to kernel.
125 static List *block_lru;
129 * Heap of entries to be invalidated.
132 static Heap *invalid_heap;
134 /* low and high-water marks for vnodes and space */
136 static u_long highvnodes, lowvnodes, current_vnodes;
137 static int64_t highbytes, lowbytes;
140 /* block size used in cache */
142 static uint64_t blocksize;
144 /* current values */
146 static int64_t usedbytes, needbytes, wantbytes;
147 static u_long usedvnodes;
149 static int64_t appendquota, appendquota_used;
151 /* Map with recovered nodes */
153 static uint32_t maxrecovered;
155 static char *recovered_map;
158 /* Handling fair lock queue */
160 struct lock_waiter {
161 FCacheEntry *entry; /* NULL for empty slots */
162 NNPQUEUE_ENTRY(lock_waiter) queue;
163 struct {
164 unsigned gcp : 1; /* Accepts gc-flagged entry? */
165 } flags;
168 NNPQUEUE_HEAD(locks_head, lock_waiter);
169 static struct locks_head *lockwaiters;
170 static unsigned num_locks;
172 static const AFSCallBack broken_callback = {0, 0, CBDROPPED};
174 static void
175 set_recovered(uint32_t index)
177 char *p;
178 u_long oldmax;
180 if (index >= maxrecovered) {
181 oldmax = maxrecovered;
182 maxrecovered = (index + 16) * 2;
183 p = realloc(recovered_map, maxrecovered);
184 if (p == NULL) {
185 arla_errx(1, ADEBERROR, "fcache: realloc %lu recovered_map failed",
186 (unsigned long)maxrecovered);
188 recovered_map = p;
189 memset(recovered_map + oldmax, 0, maxrecovered - oldmax);
191 recovered_map[index] = 1;
194 #define IS_RECOVERED(index) (recovered_map[(index)])
197 * This is how far the cleaner will go to clean out entries.
198 * The higher this is, the higher the risk is that you will
199 * lose any file that you feel is important to disconnected
200 * operation.
203 Bool fprioritylevel = arla_FPRIO_DEFAULT;
205 static uint32_t node_count = NNPFS_NO_INDEX; /* XXX */
208 * This is set to non-zero when we want to use bulkstatus(). 2 means
209 * that the nodes should be installed into the kernel.
212 static int fcache_enable_bulkstatus = 1;
213 static int fcache_bulkstatus_num = 14; /* XXX should use the [P]MTU */
215 #define FCHASHSIZE 997
218 * The cleaner
221 #define CLEANER_SLEEP 10
223 static PROCESS cleaner_pid;
225 #define CLEANER_MARKER (void*)4711
228 * The creator of nodes.
231 static PROCESS create_nodes_pid;
234 * The invalidator
237 static PROCESS invalidator_pid;
240 * Smalltalk emulation
243 int64_t
244 fcache_highbytes(void)
246 return highbytes;
249 int64_t
250 fcache_usedbytes(void)
252 return usedbytes - appendquota + appendquota_used;
255 int64_t
256 fcache_lowbytes(void)
258 return lowbytes;
261 u_long
262 fcache_highvnodes(void)
264 return highvnodes;
267 u_long
268 fcache_usedvnodes(void)
270 return usedvnodes;
273 u_long
274 fcache_lowvnodes(void)
276 return lowvnodes;
280 uint64_t
281 fcache_getblocksize(void)
283 return blocksize;
286 void
287 fcache_setblocksize(uint64_t newsize)
289 blocksize = newsize;
294 * Fair lock handling:
295 * locked nodes are flagged with `locked'
296 * nodes with lock queue are flagged with `lockwait'
299 static struct locks_head *
300 find_lock_head(FCacheEntry *node)
302 int i;
303 if (node->flags.lockwait) {
304 for (i = 0; i < num_locks; i++) {
305 struct lock_waiter *w = NNPQUEUE_FIRST(&lockwaiters[i]);
306 if (w && w->entry == node)
307 return &lockwaiters[i];
309 assert(0);
311 for (i = 0; i < num_locks; i++)
312 if (NNPQUEUE_EMPTY(&lockwaiters[i]))
313 return &lockwaiters[i];
315 assert(0); /* XXX overflow */
316 return NULL;
319 static void
320 take_lock(FCacheEntry *node)
322 assert(CheckLock(&node->lock) == 0);
323 ObtainWriteLock(&node->lock);
324 node->flags.locked = TRUE;
327 static Bool
328 fcache_islocked(FCacheEntry *e)
330 return e->flags.locked;
334 * Lock `node'
336 * Caller accepts lock on a gcp-flagged node iff `gcp' is TRUE.
339 static void
340 fcache_lock(FCacheEntry *node, Bool gcp)
342 struct locks_head *head;
343 struct lock_waiter me;
345 /* grab the lock if we can */
346 if (!node->flags.locked && (!node->flags.gcp || gcp))
347 return take_lock(node);
349 NNPQUEUE_INIT_ENTRY(&me, queue);
350 me.entry = node;
351 me.flags.gcp = gcp;
352 head = find_lock_head(node);
354 /* worker_setdebuginfo("lockwait"); */
356 node->flags.lockwait = TRUE;
357 NNPQUEUE_INSERT_TAIL(head, &me, queue);
358 LWP_WaitProcess(&me);
360 assert(node->flags.locked);
361 assert(gcp || !node->flags.gcp);
363 NNPQUEUE_REMOVE(&me, head, queue);
364 if (NNPQUEUE_EMPTY(head))
365 node->flags.lockwait = FALSE;
367 return take_lock(node);
370 static void
371 fcache_unlock(FCacheEntry *node)
373 struct locks_head *head;
374 struct lock_waiter *next = NULL;
375 struct lock_waiter *w;
377 assert(node->flags.locked);
378 AssertExclLocked(&node->lock);
379 ReleaseWriteLock(&node->lock);
381 if (!node->flags.lockwait) {
382 node->flags.locked = FALSE;
383 return;
386 head = find_lock_head(node);
387 if (node->flags.gcp) {
388 NNPQUEUE_FOREACH(w, head, queue) {
389 if (w->flags.gcp) {
390 next = w;
391 break;
394 } else {
395 next = NNPQUEUE_FIRST(head);
396 assert(next);
399 if (next)
400 LWP_NoYieldSignal(next);
401 else
402 node->flags.locked = FALSE;
406 * Allocate some appropriate append quota for nnpfs and return the
407 * value. Should be called only once.
409 * XXX config option, better defaults.
412 int64_t
413 fcache_set_appendquota(void)
415 int64_t newquota, diff;
417 assert(num_workers > 2);
419 newquota = (num_workers - 2) * blocksize;
420 assert(lowbytes > newquota);
422 if (newquota > lowbytes/2)
423 newquota = block_offset(lowbytes/3);
425 (void)fcache_need_bytes(newquota); /* XXX best effort */
427 diff = newquota - appendquota;
428 appendquota = newquota;
429 usedbytes += diff;
431 /* appendquota_used may now be larger than quota. That's ok. */
433 return diff;
437 * Counters
440 static struct {
441 unsigned long fetch_attr;
442 unsigned long fetch_attr_cached;
443 unsigned long fetch_attr_bulk;
444 unsigned long fetch_data;
445 unsigned long fetch_data_cached;
446 unsigned long store_attr;
447 unsigned long store_data;
448 } fcache_counter;
451 * Compare two entries. Return 0 if and only if the same.
454 static int
455 fcachecmp (void *a, void *b)
457 FCacheEntry *f1 = (FCacheEntry*)a;
458 FCacheEntry *f2 = (FCacheEntry*)b;
460 return VenusFid_cmp(&f1->fid, &f2->fid);
464 * Hash the value of an entry.
467 static unsigned
468 fcachehash (void *e)
470 FCacheEntry *f = (FCacheEntry*)e;
472 return f->fid.Cell + f->fid.fid.Volume + f->fid.fid.Vnode
473 + f->fid.fid.Unique;
477 * Compare expiration times.
480 static int
481 expiration_time_cmp (const void *a, const void *b)
483 const FCacheEntry *f1 = (const FCacheEntry *)a;
484 const FCacheEntry *f2 = (const FCacheEntry *)b;
486 return f1->callback.ExpirationTime - f2->callback.ExpirationTime;
489 void
490 recon_hashtabadd(FCacheEntry *entry)
492 hashtabadd(hashtab,entry);
495 void
496 recon_hashtabdel(FCacheEntry *entry)
498 hashtabdel(hashtab,entry);
502 * Globalnames
505 char **sysnamelist = NULL;
506 int sysnamenum = 0;
512 static void
513 fcache_poller_unref(FCacheEntry *e)
515 AssertExclLocked(&e->lock);
517 if (e->poll) {
518 poller_remove(e->poll);
519 e->poll = NULL;
523 static void
524 fcache_poller_reref(FCacheEntry *e, ConnCacheEntry *conn)
526 PollerEntry *pe = e->poll;
527 AssertExclLocked(&e->lock);
529 e->poll = poller_add_conn(conn);
530 if (pe)
531 poller_remove(pe);
536 * true if nobody is working on the entry
539 static Bool
540 unreferenced(FCacheEntry *e)
542 if (!fcache_islocked(e) && e->refcount == 0)
543 return TRUE;
545 return FALSE;
552 const char *
553 fcache_getdefsysname (void)
555 if (sysnamenum == 0)
556 return "fool-dont-remove-all-sysnames";
557 return sysnamelist[0];
565 fcache_setdefsysname (const char *sysname)
567 if (sysnamenum == 0)
568 return fcache_addsysname (sysname);
569 free (sysnamelist[0]);
570 sysnamelist[0] = estrdup (sysname);
571 return 0;
579 fcache_addsysname (const char *sysname)
581 sysnamenum += 1;
582 sysnamelist = erealloc (sysnamelist,
583 sysnamenum * sizeof(char *));
584 sysnamelist[sysnamenum - 1] = estrdup(sysname);
585 return 0;
593 fcache_removesysname (const char *sysname)
595 int i;
596 for (i = 0; i < sysnamenum; i++)
597 if (strcmp (sysnamelist[i], sysname) == 0)
598 break;
599 if (i == sysnamenum)
600 return 1;
601 free (sysnamelist[i]);
602 for (;i < sysnamenum; i++)
603 sysnamelist[i] = sysnamelist[i + 1];
604 sysnamenum--;
605 sysnamelist = erealloc (sysnamelist,
606 sysnamenum * sizeof(char *));
607 return 0;
611 * The node is busy, flag it and wait for signal so we can retry our
612 * operation.
615 static void
616 wait_busy(FCacheEntry *entry)
618 entry->flags.waiters = TRUE;
619 fcache_unlock(entry);
620 LWP_WaitProcess(entry);
621 fcache_lock(entry, FALSE);
625 * Wake any waiters for node and clear flag.
628 static void
629 wake_waiters(FCacheEntry *e)
631 if (e->flags.waiters) {
632 e->flags.waiters = FALSE;
633 LWP_NoYieldSignal(e);
638 * Check the wanted data range's presence in cache, return first block
639 * not already cached in *offset and *end
641 * Needed blocks found -> BLOCK_GOT
642 * No additional blocks needed -> BLOCK_NONE
643 * Only busy blocks needed -> BLOCK_BUSY
646 static BlockState
647 first_wanted_range(FCacheEntry *entry,
648 uint64_t wanted_offset, uint64_t wanted_end,
649 uint64_t *offset, uint64_t *end)
651 int busyp = 0;
652 uint64_t i;
654 AssertExclLocked(&entry->lock);
656 assert(wanted_offset <= wanted_end);
658 /* find first block that's not in cache */
659 for (i = block_offset(wanted_offset); i < wanted_end; i += blocksize) {
660 struct block *block = block_get(entry, i);
661 if (!block) {
662 *offset = i;
663 break;
665 if (block->flags.busy)
666 busyp = 1;
669 if (i >= wanted_end) {
670 if (busyp)
671 return BLOCK_BUSY;
672 else
673 return BLOCK_NONE; /* entire wanted range is already in cache */
676 /* find first block present in cache after that one */
677 for (i += blocksize; i < wanted_end; i += blocksize) {
678 if (block_get(entry, i))
679 break;
681 *end = i;
683 return BLOCK_GOT;
687 * Check the wanted data range's presence in cache.
688 * Only block presence is considered.
691 static Bool
692 fcache_have_wanted(FCacheEntry *entry, uint64_t offset, uint64_t end)
694 uint64_t i;
696 AssertExclLocked(&entry->lock);
698 assert(offset <= end);
700 for (i = block_offset(offset); i < end; i += blocksize) {
701 struct block *block = block_get(entry, i);
702 if (!block || block->flags.busy)
703 return FALSE;
706 return TRUE;
710 * return the first directory name of the cached file for `entry'
714 fcache_dir_name (FCacheEntry *entry, char *s, size_t len)
716 return snprintf (s, len, "%02X", entry->index / 0x100);
720 * return the second directory name of the cached file for `entry'.
723 static int
724 fcache_file_name (FCacheEntry *entry, char *s, size_t len)
726 return snprintf (s, len, "%02X/%02X",
727 entry->index / 0x100, entry->index % 0x100);
731 * return the file name of the cached file for `entry' and `offset'.
734 static int
735 fcache_block_name (FCacheEntry *entry, uint64_t offset, char *s, size_t len)
737 uint64_t index = offset / blocksize;
738 return snprintf (s, len, NNPFS_CACHE_FILE_PATH, /* XXX windows */
739 entry->index / 0x100, entry->index % 0x100,
740 (unsigned long long)index);
744 * the filename for the extra (converted) directory
745 * XXX no blocks here for now
748 static int
749 real_extra_file_name (FCacheEntry *entry, char *s, size_t len)
751 return snprintf(s, len, NNPFS_CACHE_DIR_PATH, /* XXX windows */
752 entry->index / 0x100, entry->index % 0x100);
756 * return the file name of the converted directory for `entry'.
760 fcache_extra_file_name (FCacheEntry *entry, char *s, size_t len)
762 assert (entry->flags.extradirp &&
763 entry->status.FileType == TYPE_DIR);
765 return real_extra_file_name (entry, s, len);
768 #if 0
770 static int fhopen_working;
773 * open file by handle
776 static int
777 fcache_fhopen (fcache_cache_handle *handle, int flags)
779 if (!handle->valid) {
780 errno = EINVAL;
781 return -1;
784 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
786 int ret;
787 fhandle_t fh;
789 memcpy (&fh, &handle->nnpfs_handle, sizeof(fh));
790 ret = fhopen (&fh, flags);
791 if (ret >= 0)
792 return ret;
794 #endif
796 errno = EINVAL;
797 return -1;
799 #endif
802 * get the handle of `filename'
806 fcache_fhget (char *filename, fcache_cache_handle *handle)
808 handle->valid = 0;
809 errno = EINVAL;
810 return -1;
812 #if 0 /* block cache */
813 #ifdef __CYGWIN32__
815 int ret, a, b;
816 char buf[1024];
818 ret = sscanf(filename, "%02X/%02X", &a, &b);
819 if (ret != 2)
820 return EINVAL;
822 GetCurrentDirectory(sizeof(buf)-1, buf);
823 buf[sizeof(buf) - 1] = '\0';
825 ret = snprintf((char *)handle->nnpfs_handle,
826 sizeof(handle->nnpfs_handle),
827 "%s\\%02X\\%02X", buf, a, b);
829 if (ret > 0 && ret < sizeof(handle->nnpfs_handle))
830 handle->valid = 1;
832 return ret;
834 #endif
836 #if 0
837 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
839 int ret;
840 fhandle_t fh;
842 ret = getfh (filename, &fh);
843 if (ret == 0) {
844 memcpy (&handle->nnpfs_handle, &fh, sizeof(fh));
845 handle->valid = 1;
848 return ret;
850 #endif
851 #endif
853 #ifdef KERBEROS
855 struct arlaViceIoctl vice_ioctl;
856 int ret;
858 if (!fhopen_working)
859 return 0;
861 vice_ioctl.in = NULL;
862 vice_ioctl.in_size = 0;
864 vice_ioctl.out = (caddr_t)&handle->nnpfs_handle;
865 vice_ioctl.out_size = sizeof(handle->nnpfs_handle);
867 ret = k_pioctl (filename, ARLA_VIOC_FHGET, (void *)&vice_ioctl, 0);
868 if (ret == 0)
869 handle->valid = 1;
871 return ret;
873 #else
874 errno = EINVAL;
875 return -1;
876 #endif
877 #endif
880 static void
881 make_dir(FCacheEntry *entry, const char *dirname)
883 int bits = 0700;
884 int ret;
885 (void)unlink(dirname); /* old implementation used files, remove if so */
887 ret = mkdir(dirname, bits);
888 if (ret < 0 && errno != EEXIST) {
889 if (errno == ENOENT) {
890 char parent[MAXPATHLEN];
892 fcache_dir_name(entry, parent, sizeof(parent));
893 ret = mkdir(parent, bits);
894 if (ret < 0)
895 arla_err (1, ADEBERROR, errno, "mkdir %s", parent);
896 ret = mkdir(dirname, bits);
897 if (ret < 0)
898 arla_err (1, ADEBERROR, errno, "mkdir %s", dirname);
899 } else {
900 arla_err (1, ADEBERROR, errno, "mkdir %s", dirname);
906 * Get ourselves a clean cache dir, creating it if necessary.
908 * We always keep the first block as arlad/nnpfs traditionally
909 * depended on there being a cache file for every node, for
910 * attributes and data length, maybe more.
912 * XXX It may be time to remove this oddity. Would it add or
913 * remove special cases?
916 static void
917 make_clean_dir(FCacheEntry *entry)
919 char name[MAXPATHLEN];
920 char dirname[MAXPATHLEN];
921 struct dirent *dp;
922 DIR *dir;
923 int ret;
925 fcache_file_name (entry, dirname, sizeof(dirname));
927 dir = opendir(dirname);
928 if (dir == NULL) {
929 make_dir(entry, dirname);
930 return;
933 while ((dp = readdir(dir)) != NULL) {
934 if (strcmp(dp->d_name, ".") == 0
935 || strcmp(dp->d_name, "..") == 0
936 || strcmp(dp->d_name, "00") == 0)
937 continue;
939 ret = snprintf(name, sizeof(name), "%s/%s", dirname, dp->d_name);
940 if (ret <= 0 || ret >= sizeof(name))
941 err (1, "dirname %s/%s", dirname, dp->d_name);
943 ret = unlink(name);
944 if (ret)
945 err (1, "unlink %s", name);
947 closedir(dir);
951 * create a new cache vnode, assume the entry is locked or private
954 static int
955 fcache_create_file (FCacheEntry *entry, int create)
957 char bname[MAXPATHLEN];
958 char extra_fname[MAXPATHLEN];
959 int fd;
961 if (create) {
962 int flags = O_RDWR | O_BINARY | O_CREAT | O_TRUNC;
964 if (use_o_largefile)
965 flags |= O_LARGEFILE;
967 make_clean_dir(entry);
969 fcache_block_name (entry, 0, bname, sizeof(bname));
970 fd = open(bname, flags, 0600);
971 if (fd < 0)
972 arla_err (1, ADEBERROR, errno, "open %s", bname);
973 if (close (fd) < 0)
974 arla_err (1, ADEBERROR, errno, "close %s", bname);
977 real_extra_file_name (entry, extra_fname, sizeof(extra_fname));
978 unlink (extra_fname);
979 return 0;
983 * Transfer used append quota to ordinary usage. `e' will be flagged
984 * with gcp and unlocked temporarily, this is to keep it from being
985 * locked by other operations while we may need to gc blocks from it.
987 * Give back quota to kernel, if any, and return error code.
991 fcache_update_appendquota(FCacheEntry *e)
993 int64_t diff;
994 static int nthreads;
995 int ret = 0;
997 AssertExclLocked(&e->lock);
998 assert(diff >= 0);
1000 if (!appendquota_used)
1001 return 0;
1003 worker_setdebuginfo("gc");
1004 assert(!e->flags.gcp);
1006 e->flags.gcp = TRUE;
1007 fcache_unlock(e);
1009 do {
1010 if (!nthreads) {
1011 diff = appendquota_used;
1012 if (!diff)
1013 return 0;
1014 } else {
1015 diff = blocksize;
1018 nthreads++;
1019 ret = fcache_want_bytes(diff);
1020 nthreads--;
1021 } while (ret && !nthreads);
1023 fcache_lock(e, TRUE);
1024 e->flags.gcp = FALSE;
1026 LWP_NoYieldSignal(e);
1028 worker_setdebuginfo("not gc");
1030 if (ret)
1031 return 0;
1033 assert(appendquota_used >= 0);
1035 if (appendquota_used < diff)
1036 diff = appendquota_used;
1037 appendquota_used -= diff;
1038 usedbytes += diff;
1040 assert(appendquota_used >= 0);
1041 /* assert(appendquota_used <= appendquota); not on cache size change */
1043 return install_appendquota(diff);
1047 * Create a block and set kernelp.
1051 fcache_append_block(FCacheEntry *e, uint64_t offset)
1053 struct block *b;
1054 assert(block_offset(offset) == offset);
1056 appendquota_used += blocksize;
1057 e->usage += blocksize;
1058 /* assert(appendquota_used <= appendquota); not on cache size change */
1060 b = block_add(e, offset);
1061 b->flags.kernelp = TRUE;
1062 b->lru_le = listaddhead(kernel_block_lru, b);
1063 assert(b->lru_le);
1065 return 0;
1069 * Create a block, setting kernelp as indicated.
1072 static int
1073 create_block(FCacheEntry *e, uint64_t offset)
1075 struct block *b;
1076 int ret;
1077 assert(block_offset(offset) == offset);
1079 worker_setdebuginfo("gc");
1081 ret = fcache_update_usage(e, 1);
1083 worker_setdebuginfo("not gc");
1085 if (ret)
1086 return ret;
1088 b = block_add(e, offset);
1089 b->flags.kernelp = FALSE;
1090 b->lru_le = listaddhead(block_lru, b);
1092 assert(b->lru_le);
1094 return ret;
1098 * Return true if the node exists.
1101 Bool
1102 fcache_block_exists(FCacheEntry *entry, uint64_t offset)
1104 struct block *block = block_get(entry, offset);
1105 if (!block)
1106 return FALSE;
1108 return TRUE;
1112 * return a fd to the cache file of `entry' or -1 on failure
1116 fcache_open_block(FCacheEntry *entry, uint64_t offset, Bool writep)
1118 char fname[MAXPATHLEN];
1119 int flags = O_BINARY;
1120 Bool exists;
1122 if (use_o_largefile)
1123 flags |= O_LARGEFILE;
1125 if (writep)
1126 flags |= O_RDWR;
1127 else
1128 flags |= O_RDONLY;
1130 exists = fcache_block_exists(entry, offset);
1131 if (writep) {
1132 if (!exists) {
1133 int ret = create_block(entry, offset);
1134 if (ret) {
1135 errno = ret;
1136 return -1;
1139 flags |= O_CREAT;
1141 } else {
1142 if (!exists) {
1143 arla_warnx (ADEBWARN, "Tried to read nonexistent block");
1144 assert(0);
1145 errno = EIO;
1146 return -1;
1150 #if 0
1151 if (fhopen_working) {
1152 ret = fcache_fhopen (&entry->handle, flags);
1153 if (ret < 0 && (errno == EINVAL || errno == EPERM))
1154 fhopen_working = 0;
1155 else
1156 return ret;
1158 #endif
1160 fcache_block_name(entry, offset, fname, sizeof(fname));
1161 return open(fname, flags, 0600);
1165 * Create blocks for a new range without accounting, mark them busy.
1166 * Returns the number of blocks created (for future accounting).
1169 static int
1170 create_busy_blocks(FCacheEntry *entry, uint64_t offset, uint64_t end)
1172 char fname[MAXPATHLEN];
1173 struct block *b;
1174 uint64_t off;
1175 int fd;
1176 int i = 0;
1178 AssertExclLocked(&entry->lock);
1180 for (off = offset; off < end; off += blocksize) {
1181 fcache_block_name(entry, off, fname, sizeof(fname));
1182 fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
1183 if (fd < 0) {
1184 fd = errno;
1185 arla_warnx(ADEBWARN, "create_busy_blocks: "
1186 "creat failed at offset 0x%llx\n",
1187 (unsigned long long)off);
1188 errno = fd;
1189 return -1;
1191 close(fd);
1192 b = block_add(entry, off);
1193 b->lru_le = listaddhead(block_lru, b);
1194 b->flags.busy = TRUE;
1195 i++;
1198 return i;
1202 * Clean up after create_busy_blocks(), removing any blocks beyond
1203 * what was actually received. Iff `usagep' is set, update cache
1204 * usage.
1207 static void
1208 delete_unbusy_blocks(FCacheEntry *entry, uint64_t offset,
1209 uint64_t end, uint64_t guessed_end, Bool usagep)
1211 uint64_t off;
1213 fcache_data_setbusy(entry, offset, end, FALSE);
1215 for (off = end; off < guessed_end; off += blocksize) {
1216 struct block *b = block_get(entry, off);
1217 setbusy_block(b, FALSE);
1218 throw_block(b, usagep);
1220 wake_waiters(entry);
1224 * return a fd to the converted directory for `entry'
1228 fcache_open_extra_dir (FCacheEntry *entry, int flag, mode_t mode)
1230 char fname[MAXPATHLEN];
1232 assert (entry->flags.extradirp &&
1233 entry->status.FileType == TYPE_DIR);
1235 fcache_extra_file_name (entry, fname, sizeof(fname));
1236 return open (fname, flag | O_BINARY, mode);
1243 uint64_t
1244 fcache_get_status_length(const AFSFetchStatus *status)
1246 return status->Length | ((uint64_t)status->LengthHigh << 32);
1249 void
1250 fcache_set_status_length(AFSFetchStatus *status, int64_t length)
1252 status->Length = length & 0xffffffff;
1253 status->LengthHigh = length >> 32;
1257 * Discard a cached data block for `entry'.
1260 static void
1261 throw_block(struct block *b, Bool usagep)
1263 FCacheEntry *entry = b->node;
1264 char fname[MAXPATHLEN];
1265 uint64_t offset = b->offset;
1267 AssertExclLocked(&entry->lock);
1268 assert(!b->flags.busy);
1270 if (b->flags.kernelp)
1271 listdel(kernel_block_lru, b->lru_le);
1272 else
1273 listdel(block_lru, b->lru_le);
1275 block_free(b);
1276 fcache_block_name(entry, offset, fname, sizeof(fname));
1278 if (offset == 0)
1279 truncate(fname, 0);
1280 else
1281 unlink(fname);
1283 if (usagep)
1284 (void)fcache_update_usage(entry, -1);
1288 * Discard a cached data block for `entry' and update accounting.
1291 void
1292 fcache_throw_block(struct block *b)
1294 throw_block(b, TRUE);
1298 * Discard the data cached for `entry'.
1301 static void
1302 throw_data (FCacheEntry *entry)
1304 int ret;
1306 AssertExclLocked(&entry->lock);
1307 assert (entry->flags.usedp);
1308 assert(usedbytes >= entry->usage);
1310 entry->flags.stale = FALSE;
1311 entry->flags.dirtied = FALSE;
1313 arla_log(ADEBVLOG, "throw_data(%d, %u, %u, %u): usage %llu",
1314 entry->fid.Cell, entry->fid.fid.Volume,
1315 entry->fid.fid.Vnode, entry->fid.fid.Unique,
1316 (unsigned long long)entry->usage);
1318 if (block_emptyp(entry)) {
1319 assert(entry->usage == 0);
1320 return;
1323 ret = abuf_purge(entry);
1324 if (ret) {
1325 /* XXX set dirtied/stale again? */
1326 arla_warn (ADEBFCACHE, ret, "abuf_purge");
1327 goto out;
1330 assert(block_emptyp(entry));
1332 if (entry->flags.extradirp) {
1333 char fname[MAXPATHLEN];
1335 fcache_extra_file_name (entry, fname, sizeof(fname));
1336 unlink (fname);
1337 entry->flags.extradirp = FALSE;
1340 assert(entry->usage == 0);
1342 out:
1343 cm_check_consistency();
1347 * A probe function for a file server.
1351 fs_probe (struct rx_connection *conn)
1353 uint32_t sec, usec;
1355 return RXAFS_GetTime (conn, &sec, &usec);
1359 * Clean a locked node.
1361 * The caller must not touch the node afterwards.
1362 * This function may yield.
1365 static void
1366 throw_entry (FCacheEntry *entry)
1368 CredCacheEntry *ce;
1369 ConnCacheEntry *conn;
1370 AFSCBFids fids;
1371 AFSCBs cbs;
1372 int ret;
1374 assert (entry->flags.usedp);
1375 assert (!entry->flags.kernelp);
1377 AssertExclLocked(&entry->lock);
1378 assert(LockWaiters(&entry->lock) == 0);
1379 assert(entry->refcount == 0);
1381 hashtabdel (hashtab, entry);
1382 listdel(node_lru, entry->lru_le);
1384 throw_data (entry);
1386 if (entry->invalid_ptr != -1) {
1387 heap_remove (invalid_heap, entry->invalid_ptr);
1388 entry->invalid_ptr = -1;
1391 fcache_poller_unref(entry);
1393 if (entry->flags.attrp && !entry->flags.silly && entry->host) {
1394 ce = cred_get (entry->fid.Cell, 0, CRED_NONE);
1395 assert (ce != NULL);
1397 conn = conn_get (entry->fid.Cell, entry->host, afsport,
1398 FS_SERVICE_ID, fs_probe, ce);
1399 cred_free (ce);
1401 fids.len = cbs.len = 1;
1402 fids.val = &entry->fid.fid;
1403 cbs.val = &entry->callback;
1405 if (conn_isalivep (conn)) {
1406 ret = RXAFS_GiveUpCallBacks(conn->connection, &fids, &cbs);
1407 if (host_downp(ret)) {
1408 conn_dead (conn);
1409 ret = ENETDOWN;
1411 } else
1412 ret = ENETDOWN;
1413 conn_free (conn);
1414 if (ret)
1415 arla_warn (ADEBFCACHE, ret, "RXAFS_GiveUpCallBacks");
1417 if (entry->volume) {
1418 volcache_free (entry->volume);
1419 entry->volume = NULL;
1421 assert_not_flag(entry,kernelp);
1422 entry->flags.attrp = FALSE;
1423 entry->flags.usedp = FALSE;
1424 entry->lru_le = listaddtail(free_nodes, entry);
1425 assert(entry->lru_le);
1426 --usedvnodes;
1427 fcache_unlock(entry);
1428 LWP_NoYieldSignal(free_nodes);
1432 * Return the next cache node number.
1435 static uint32_t
1436 next_cache_index (void)
1438 do {
1439 node_count++;
1440 } while ((node_count < maxrecovered)
1441 && IS_RECOVERED(node_count));
1443 return node_count;
1447 * Pre-create cache nodes up to the limit highvnodes. If you want to
1448 * create more increase highnodes and signal create_nodes.
1451 static void
1452 create_nodes (char *arg)
1454 FCacheEntry *entries;
1455 unsigned count = 0;
1456 struct timeval tv;
1458 while (1) {
1459 unsigned int n, i, j;
1461 while (highvnodes <= current_vnodes)
1462 LWP_WaitProcess (create_nodes);
1464 n = highvnodes - current_vnodes;
1466 count = 0;
1468 arla_warnx (ADEBFCACHE,
1469 "pre-creating nodes");
1471 entries = calloc (n, sizeof(FCacheEntry));
1472 if (n != 0 && entries == NULL)
1473 arla_errx (1, ADEBERROR, "fcache: calloc failed");
1475 for (i = 0; i < n; ++i) {
1476 entries[i].invalid_ptr = -1;
1477 entries[i].volume = NULL;
1478 entries[i].refcount = 0;
1479 entries[i].anonaccess = 0;
1480 entries[i].poll = NULL;
1481 for (j = 0; j < NACCESS; j++) {
1482 entries[i].acccache[j].cred = ARLA_NO_AUTH_CRED;
1483 entries[i].acccache[j].access = 0;
1485 entries[i].usage = 0;
1486 entries[i].blocks = listnew();
1487 assert(entries[i].blocks);
1489 Lock_Init(&entries[i].lock);
1490 entries[i].index = next_cache_index ();
1491 fcache_create_file (&entries[i], 1);
1493 current_vnodes++;
1495 ++count;
1496 tv.tv_sec = 0;
1497 tv.tv_usec = 1000;
1499 entries[i].lru_le = listaddhead(free_nodes, &entries[i]);
1500 assert (entries[i].lru_le);
1502 LWP_NoYieldSignal (free_nodes);
1503 IOMGR_Select(0, NULL, NULL, NULL, &tv);
1506 arla_warnx (ADEBFCACHE,
1507 "pre-created %u nodes", count);
1512 * This is the almighty cleaner loop
1515 static Bool cleaner_working = FALSE;
1516 static Bool cleaner_force = FALSE;
1517 static int cleaner_npending;
1520 * manage count of pending gc ops.
1523 void
1524 fcache_cleaner_ref(void)
1526 cleaner_npending++;
1529 void
1530 fcache_cleaner_deref(void)
1532 cleaner_npending--;
1533 assert(cleaner_npending >= 0);
1534 if (!cleaner_npending)
1535 LWP_NoYieldSignal(&cleaner_npending);
1539 * make sure we wait until all ops triggered by the gc message have
1540 * been processed. putdata does take some time, should be batched.
1543 static void
1544 cleaner_gc_wait(void)
1546 IOMGR_Poll();
1547 while (cleaner_npending)
1548 LWP_WaitProcess(&cleaner_npending);
1552 * Try to trim nodes off the node LRU:s until we reach the low water
1553 * mark.
1556 static void
1557 cleaner_gc_nodes(void)
1559 struct nnpfs_message_gc msg;
1560 Listitem *item, *prev;
1561 FCacheEntry *entry;
1562 Listitem *kmarker;
1564 int numnodes = NNPFS_GC_MAX_HANDLE;
1565 int cnt = 0;
1566 Bool found_kmarker = FALSE;
1568 kmarker = listaddhead(kernel_node_lru, CLEANER_MARKER);
1569 assert(kmarker);
1572 * XXX it would be better if kernel handled this
1574 for (item = listtail(kernel_node_lru);
1575 item && item != kmarker;
1576 item = prev) {
1577 prev = listprev(kernel_node_lru, item);
1578 entry = (FCacheEntry *)listdata(item);
1580 if (entry->flags.silly && unreferenced(entry)) {
1581 memcpy(&msg.handle[cnt].node, &entry->fid,
1582 sizeof(msg.handle[0].node));
1583 msg.handle[cnt].offset = NNPFS_NO_OFFSET;
1584 cnt++;
1586 if (cnt >= numnodes) {
1587 nnpfs_send_message_gc(kernel_fd, &msg, cnt);
1588 cnt = 0;
1593 if (cnt > 0) {
1594 nnpfs_send_message_gc(kernel_fd, &msg, cnt);
1595 cleaner_gc_wait();
1596 cnt = 0;
1600 while (usedvnodes > lowvnodes) {
1601 Listitem *nmarker = listaddhead(node_lru, CLEANER_MARKER);
1602 assert(nmarker);
1604 while ((item = listtail(node_lru)) != NULL
1605 && item != nmarker
1606 && usedvnodes > lowvnodes) {
1607 entry = (FCacheEntry *)listdata(item);
1609 if (unreferenced(entry)) {
1610 /* if (this_is_a_good_node_to_gc(entry,state)) */
1611 /* XXX fprio */
1613 fcache_lock(entry, FALSE); /* should be clean */
1614 throw_entry(entry); /* releases lock and sleeps */
1615 } else {
1616 fcache_node_lru(entry);
1619 listdel(node_lru, nmarker);
1621 if (found_kmarker)
1622 break;
1624 for (item = listtail(kernel_node_lru);
1625 item && item != kmarker && usedvnodes > lowvnodes;
1626 item = prev) {
1627 prev = listprev(kernel_node_lru, item);
1628 entry = (FCacheEntry *)listdata(item);
1630 if (unreferenced(entry)) {
1631 /* if (this_is_a_good_node_to_gc(entry,state)) */
1632 /* XXX fprio */
1634 fcache_node_lru(entry);
1636 memcpy(&msg.handle[cnt].node, &entry->fid,
1637 sizeof(msg.handle[0].node));
1638 msg.handle[cnt].offset = NNPFS_NO_OFFSET;
1639 cnt++;
1641 if (cnt >= numnodes)
1642 break;
1646 if (cnt > 0) {
1647 nnpfs_send_message_gc(kernel_fd, &msg, cnt);
1648 cleaner_gc_wait();
1649 cnt = 0;
1652 /* We've traversed the entire kernel node list, time to bail out. */
1653 if (item == kmarker)
1654 found_kmarker = TRUE;
1657 listdel(kernel_node_lru, kmarker);
1661 * Return true if this looks like a good block to gc.
1664 static Bool
1665 good_block_to_gc(struct block *b)
1667 FCacheEntry *entry = b->node;
1669 if (fcache_islocked(entry))
1670 return FALSE;
1672 if (b->flags.busy) {
1673 #if 0
1674 arla_warnx(ADEBWARN, "busy block (%ld.%lu.%lu.%lu) @%llu",
1675 (long)entry->fid.Cell,
1676 (unsigned long)entry->fid.fid.Volume,
1677 (unsigned long)entry->fid.fid.Vnode,
1678 (unsigned long)entry->fid.fid.Unique,
1679 (unsigned long long)b->offset);
1680 #endif
1681 return FALSE;
1684 if (entry->flags.silly && entry->flags.kernelp)
1685 return FALSE;
1687 if (entry->status.FileType != TYPE_DIR)
1688 return TRUE;
1690 if (!entry->flags.datausedp)
1691 return TRUE;
1693 if (cleaner_force)
1694 return TRUE;
1696 return FALSE;
1700 * GC block if it's possible and looks like a good idea.
1702 * Return true if the block is gone.
1705 static Bool
1706 gc_block_maybe(struct block *b)
1708 if (good_block_to_gc(b)) {
1709 FCacheEntry *entry = b->node;
1711 /* For directories, throw all or nothing */
1712 if (entry->status.FileType == TYPE_DIR) {
1713 if (unreferenced(entry)) {
1714 fcache_lock(entry, FALSE); /* should be clean */
1715 throw_data(entry);
1716 fcache_unlock(entry);
1718 } else {
1719 Bool locked = fcache_islocked(entry);
1720 if (!locked)
1721 fcache_lock(entry, TRUE);
1723 throw_block(b, TRUE);
1725 if (!locked)
1726 fcache_unlock(entry);
1728 return TRUE;
1730 return FALSE;
1734 * Return target value for usedbytes.
1737 static int64_t
1738 target_usedbytes(void) {
1739 return min(lowbytes, highbytes - needbytes - wantbytes);
1743 * Get more space by evicting blocks off of the block LRU lists.
1745 * XXX don't loop forever
1748 static void
1749 cleaner_gc_blocks(void)
1751 struct nnpfs_message_gc msg;
1752 struct block *entry;
1753 Listitem *item, *prev;
1754 Listitem *marker;
1755 int64_t usedtarget = target_usedbytes();
1757 int numblocks = NNPFS_GC_MAX_HANDLE;
1758 int cnt = 0;
1759 Bool found_marker = FALSE;
1761 marker = listaddhead(kernel_block_lru, CLEANER_MARKER);
1762 assert(marker);
1764 while (usedbytes > usedtarget) {
1765 Listitem *last = NULL;
1768 * First, release non-kernel blocks. No context switching in
1769 * this loop. Directory blocks may be thrown several at a
1770 * time, so we need to take extra care. Fortunately, blocks
1771 * we've already passed and not thrown can be trusted as long
1772 * as we avoid context switches.
1774 while (usedbytes > usedtarget) {
1775 if (last)
1776 item = listprev(block_lru, last);
1777 else
1778 item = listtail(block_lru);
1780 if (!item)
1781 break;
1783 entry = (struct block *)listdata(item);
1785 if (!gc_block_maybe(entry))
1786 last = item;
1789 if (found_marker)
1790 break;
1792 for (item = listtail(kernel_block_lru);
1793 item && item != marker && usedbytes > usedtarget;
1794 item = prev) {
1795 prev = listprev(kernel_block_lru, item);
1796 entry = (struct block *)listdata(item);
1798 if (good_block_to_gc(entry)) {
1799 listdel(kernel_block_lru, item);
1800 entry->lru_le = listaddhead(kernel_block_lru, entry);
1802 memcpy(&msg.handle[cnt].node, &entry->node->fid,
1803 sizeof(msg.handle[0].node));
1806 * XXX overkill to gc entire node for dirs. invalidnode?
1808 if (entry->node->status.FileType == TYPE_DIR)
1809 msg.handle[cnt].offset = NNPFS_NO_OFFSET;
1810 else
1811 msg.handle[cnt].offset = entry->offset;
1813 cnt++;
1814 if (cnt >= numblocks)
1815 break;
1819 if (cnt > 0) {
1820 nnpfs_send_message_gc(kernel_fd, &msg, cnt);
1821 cleaner_gc_wait();
1822 usedtarget = target_usedbytes();
1823 cnt = 0;
1826 /* We've traversed the entire kernel node list, time to bail out. */
1827 if (item == marker)
1828 found_marker = TRUE;
1831 listdel(kernel_block_lru, marker);
1834 static void
1835 cleaner (char *arg)
1837 while (TRUE) {
1838 int i;
1839 arla_warnx (ADEBCLEANER,
1840 "running cleaner: "
1841 "%lu (%lu-(%lu)-%lu) files, "
1842 "%lu (%lu-%lu) bytes "
1843 "%lu needed bytes "
1844 "%lu wanted bytes",
1845 usedvnodes, lowvnodes, current_vnodes, highvnodes,
1846 (long)usedbytes, (long)lowbytes, (long)highbytes,
1847 (long)needbytes, (long)wantbytes);
1848 cleaner_working = TRUE;
1850 for (i = 0; i < 3; i++) {
1852 * Releasing nodes may give us data space as a side effect, so we
1853 * check the nodes first.
1855 cm_check_consistency();
1857 if (i == 1)
1858 cleaner_force = TRUE;
1859 else
1860 cleaner_force = FALSE;
1862 cleaner_gc_nodes();
1863 cm_check_consistency();
1864 cleaner_gc_blocks();
1866 if (target_usedbytes() >= usedbytes)
1867 break;
1870 arla_warnx(ADEBCLEANER,
1871 "cleaner done: "
1872 "%lu (%lu-(%lu)-%lu) files, "
1873 "%ld (%ld-%ld) bytes "
1874 "%ld needed bytes "
1875 "%lu wanted bytes",
1876 usedvnodes, lowvnodes, current_vnodes, highvnodes,
1877 (long)usedbytes, (long)lowbytes, (long)highbytes,
1878 (long)needbytes, (long)wantbytes);
1880 cm_check_consistency();
1881 if (needbytes || wantbytes)
1882 LWP_NoYieldSignal(fcache_need_bytes);
1883 cleaner_working = FALSE;
1884 IOMGR_Sleep (CLEANER_SLEEP);
1888 static void
1889 fcache_wakeup_cleaner (void *wait)
1891 worker_setdebuginfo("wake cleaner");
1892 if (cleaner_working == FALSE)
1893 IOMGR_Cancel (cleaner_pid);
1894 worker_setdebuginfo("wait cleaner");
1895 LWP_WaitProcess (wait);
1896 worker_setdebuginfo("waited cleaner");
1900 * Try to allocate 'wanted' bytes of space. May trigger GC.
1903 static int
1904 fcache_want_bytes(uint64_t wanted)
1906 int ret = 0;
1907 wantbytes += wanted;
1909 assert(wantbytes >= 0);
1911 if (usedbytes + needbytes + wantbytes > highbytes)
1912 fcache_wakeup_cleaner(fcache_need_bytes);
1914 if (usedbytes + needbytes + wantbytes > highbytes)
1915 ret = ENOSPC;
1917 wantbytes -= wanted;
1918 return ret;
1922 * Reserve 'needed' bytes of space. May trigger GC.
1925 static int
1926 fcache_need_bytes(uint64_t needed)
1928 int ret = 0;
1929 needbytes += needed;
1931 assert(needbytes >= 0);
1933 if (usedbytes + needbytes > highbytes)
1934 fcache_wakeup_cleaner(fcache_need_bytes);
1936 if (usedbytes + needbytes > highbytes) {
1937 arla_warnx(ADEBWARN,
1938 "Out of space, couldn't get needed bytes after cleaner "
1939 "(%lu bytes missing, %lu used, %lu highbytes)",
1940 (long)(needbytes - (highbytes - usedbytes)),
1941 (long)usedbytes, (long)highbytes);
1942 ret = ENOSPC;
1945 needbytes -= needed;
1947 return ret;
1951 * Run through the heap of objects to be invalidated and throw them away
1952 * when their expirationtime arrive.
1955 static void
1956 invalidator (char *arg)
1958 for (;;) {
1959 const void *head;
1960 struct timeval tv;
1962 arla_warnx(ADEBINVALIDATOR,
1963 "running invalidator");
1965 while ((head = heap_head (invalid_heap)) == NULL)
1966 LWP_WaitProcess (invalid_heap);
1968 gettimeofday (&tv, NULL);
1970 while ((head = heap_head (invalid_heap)) != NULL) {
1971 FCacheEntry *entry = (FCacheEntry *)head;
1973 if (tv.tv_sec < entry->callback.ExpirationTime) {
1974 unsigned long t = entry->callback.ExpirationTime - tv.tv_sec;
1976 arla_warnx (ADEBINVALIDATOR,
1977 "invalidator: sleeping for %lu second(s)", t);
1978 IOMGR_Sleep (t);
1979 break;
1982 fcache_lock(entry, FALSE);
1983 if (head == heap_head (invalid_heap)) {
1984 heap_remove_head (invalid_heap);
1985 entry->invalid_ptr = -1;
1986 if (entry->flags.kernelp)
1987 break_callback (entry);
1988 fcache_poller_unref(entry);
1990 fcache_unlock(entry);
1996 * Add `entry' to the list of entries to invalidate when its time is
1997 * up.
2000 static void
2001 add_to_invalidate (FCacheEntry *e)
2003 if (e->invalid_ptr != -1)
2004 heap_remove (invalid_heap, e->invalid_ptr);
2005 heap_insert (invalid_heap, (const void *)e, &e->invalid_ptr);
2006 LWP_NoYieldSignal (invalid_heap);
2007 IOMGR_Cancel(invalidator_pid);
2011 * Return a usable locked entry.
2012 * If there are no free entries, sleep until there is.
2015 static FCacheEntry *
2016 find_free_entry (void)
2018 FCacheEntry *entry = NULL;
2019 Listitem *item;
2021 while ((item = listtail(free_nodes)) == NULL) {
2022 arla_warnx (ADEBFCACHE, "find_free_entry: sleeping");
2023 fcache_wakeup_cleaner(free_nodes);
2026 /* Entries on the freelist should not be locked. */
2027 entry = (FCacheEntry *)listdata(item);
2028 assert_not_flag(entry,usedp);
2029 fcache_lock(entry, FALSE); /* should be clean */
2030 listdel(free_nodes, entry->lru_le);
2031 entry->lru_le = NULL;
2033 ++usedvnodes;
2035 return entry;
2042 struct fstore_context {
2043 Listitem *item;
2044 unsigned n;
2047 static int
2048 fcache_store_entry (struct fcache_store *st, void *ptr)
2050 struct fstore_context *c;
2051 FCacheEntry *entry;
2053 c = (struct fstore_context *)ptr;
2054 if (c->item == NULL) /* check if done ? */
2055 return STORE_DONE;
2057 entry = (FCacheEntry *)listdata (c->item);
2058 c->item = listprev (node_lru, c->item);
2060 if (!entry->flags.usedp)
2061 return STORE_SKIP;
2063 strlcpy(st->cell, cell_num2name(entry->fid.Cell), sizeof(st->cell));
2064 st->fid = entry->fid.fid;
2065 st->refcount = entry->refcount;
2066 st->length = entry->usage;
2067 st->fetched_length = 0; /* XXX */
2068 st->volsync = entry->volsync;
2069 st->status = entry->status;
2070 st->anonaccess = entry->anonaccess;
2071 st->index = entry->index;
2072 st->flags.attrp = entry->flags.attrp;
2073 st->flags.datap = entry->usage ? TRUE : FALSE;
2074 st->flags.extradirp = entry->flags.extradirp;
2075 st->flags.mountp = entry->flags.mountp;
2076 st->flags.fake_mp = entry->flags.fake_mp;
2077 st->flags.vol_root = entry->flags.vol_root;
2078 strlcpy(st->parentcell, cell_num2name(entry->parent.Cell),
2079 sizeof(st->parentcell));
2080 st->parent = entry->parent.fid;
2081 st->priority = entry->priority;
2083 c->n++;
2084 return STORE_NEXT;
2092 fcache_store_state (void)
2094 struct fstore_context c;
2095 int ret;
2097 if (node_lru == NULL) {
2098 arla_warnx (ADEBFCACHE, "store_state: node_lru is NULL\n");
2099 return 0;
2102 c.item = listtail(node_lru);
2103 c.n = 0;
2105 ret = state_store_fcache("fcache", fcache_store_entry, &c);
2106 if (ret)
2107 arla_warn(ADEBWARN, ret, "failed to write fcache state");
2108 else
2109 arla_warnx (ADEBFCACHE, "wrote %u entries to fcache", c.n);
2111 return 0;
2118 static int
2119 fcache_recover_entry (struct fcache_store *st, void *ptr)
2121 unsigned *n = (unsigned *)ptr;
2123 CredCacheEntry *ce;
2124 FCacheEntry *e;
2125 int i;
2126 VolCacheEntry *vol;
2127 int res;
2128 int32_t cellid;
2130 cellid = cell_name2num(st->cell);
2131 assert (cellid != -1);
2133 ce = cred_get (cellid, 0, 0);
2134 assert (ce != NULL);
2136 res = volcache_getbyid (st->fid.Volume, cellid, ce, &vol, NULL);
2137 cred_free (ce);
2138 if (res)
2139 return 0;
2140 assert(vol);
2142 e = calloc(1, sizeof(FCacheEntry));
2143 e->invalid_ptr = -1;
2144 Lock_Init(&e->lock);
2145 fcache_lock(e, FALSE);
2148 e->fid.Cell = cellid;
2149 e->fid.fid = st->fid;
2150 e->host = 0;
2151 e->status = st->status;
2152 e->usage = st->length;
2153 /* e->fetched_length = st->fetched_length; */
2154 e->callback = broken_callback;
2155 e->volsync = st->volsync;
2156 e->refcount = st->refcount;
2158 /* Better not restore the rights. pags don't have to be the same */
2159 for (i = 0; i < NACCESS; ++i) {
2160 e->acccache[i].cred = ARLA_NO_AUTH_CRED;
2161 e->acccache[i].access = ANONE;
2164 e->anonaccess = st->anonaccess;
2165 e->index = st->index;
2166 fcache_create_file(e, 0);
2167 set_recovered(e->index);
2168 e->flags.usedp = TRUE;
2169 e->flags.attrp = st->flags.attrp;
2170 /* st->flags.datap */
2171 e->flags.attrusedp = FALSE;
2172 e->flags.datausedp = FALSE;
2173 e->flags.kernelp = FALSE;
2174 e->flags.extradirp = st->flags.extradirp;
2175 e->flags.mountp = st->flags.mountp;
2176 e->flags.fake_mp = st->flags.fake_mp;
2177 e->flags.vol_root = st->flags.vol_root;
2178 e->flags.sentenced = FALSE;
2179 e->flags.stale = FALSE;
2180 e->flags.dirtied = FALSE;
2181 e->flags.silly = FALSE;
2182 e->flags.waiters = FALSE;
2183 e->flags.gcp = FALSE;
2184 e->flags.appended = FALSE;
2185 e->tokens = 0;
2186 e->parent.Cell = cell_name2num(st->parentcell);
2187 assert(e->parent.Cell != -1);
2188 e->parent.fid = st->parent;
2189 e->priority = st->priority;
2190 e->hits = 0;
2191 e->lru_le = listaddhead (node_lru, e);
2192 assert(e->lru_le);
2193 e->volume = vol;
2194 hashtabadd (hashtab, e);
2195 if (e->usage)
2196 usedbytes += e->usage;
2197 fcache_unlock(e);
2199 (*n)++;
2201 return 0;
2208 static void
2209 fcache_recover_state (void)
2211 unsigned n;
2213 n = 0;
2214 state_recover_fcache("fcache", fcache_recover_entry, &n);
2216 arla_warnx (ADEBFCACHE, "recovered %u entries to fcache", n);
2217 current_vnodes = n;
2221 * Search for `cred' in `ae' and return a pointer in `pos'. If it
2222 * already exists return TRUE, else return FALSE and set pos to a
2223 * random slot.
2226 Bool
2227 findaccess (nnpfs_pag_t cred, AccessEntry *ae, AccessEntry **pos)
2229 int i;
2231 for(i = 0; i < NACCESS ; ++i)
2232 if(ae[i].cred == cred) {
2233 *pos = &ae[i];
2234 return TRUE;
2237 i = rand() % NACCESS;
2238 *pos = &ae[i];
2239 return FALSE;
2247 static int
2248 fs_rtt_cmp (const void *v1, const void *v2)
2250 struct fs_server_entry *e1 = (struct fs_server_entry *)v1;
2251 struct fs_server_entry *e2 = (struct fs_server_entry *)v2;
2253 return conn_rtt_cmp(&e1->conn, &e2->conn);
2257 * Initialize a `fs_server_context'.
2260 static void
2261 init_fs_server_context (fs_server_context *context)
2263 context->num_conns = 0;
2266 static long
2267 find_partition (fs_server_context *context)
2269 int i = context->conns[context->i - 1].ve_ent;
2271 if (i < 0 || i >= context->ve->entry.nServers)
2272 return 0;
2273 return context->ve->entry.serverPartition[i];
2277 * Find the next fileserver for the request in `context'.
2278 * Returns a ConnCacheEntry or NULL.
2281 ConnCacheEntry *
2282 find_next_fs (fs_server_context *context,
2283 ConnCacheEntry *prev_conn,
2284 int error)
2286 if (error) {
2287 if (host_downp(error))
2288 conn_dead (prev_conn);
2289 if (volume_downp(error))
2290 volcache_mark_down (context->ve,
2291 context->conns[context->i - 1].ve_ent,
2292 error);
2293 } else if (prev_conn) {
2294 assert(prev_conn == context->conns[context->i - 1].conn);
2295 volcache_reliable_el(context->ve, context->conns[context->i - 1].ve_ent);
2298 if (context->i < context->num_conns)
2299 return context->conns[context->i++].conn;
2300 else
2301 return NULL;
2305 * Clean up a `fs_server_context'
2308 void
2309 free_fs_server_context (fs_server_context *context)
2311 int i;
2313 for (i = 0; i < context->num_conns; ++i)
2314 conn_free (context->conns[i].conn);
2316 if (context->ve)
2317 volcache_process_marks(context->ve);
2321 * Find the the file servers housing the volume for `e' and store it
2322 * in the `context'.
2326 init_fs_context (FCacheEntry *e,
2327 CredCacheEntry *ce,
2328 fs_server_context *context)
2330 VolCacheEntry *ve = e->volume;
2331 int i;
2332 int bit;
2333 int num_clones;
2334 int cell = e->fid.Cell;
2335 int ret;
2337 memset(context, 0, sizeof(*context));
2339 if (ve == NULL) {
2340 ret = volcache_getbyid (e->fid.fid.Volume, e->fid.Cell,
2341 ce, &e->volume, NULL);
2342 if (ret)
2343 return ret;
2344 ve = e->volume;
2347 ret = volume_make_uptodate (ve, ce);
2348 if (ret)
2349 return ret;
2351 bit = volcache_volid2bit (ve, e->fid.fid.Volume);
2353 if (bit == -1) {
2354 /* the volume entry is inconsistent. */
2355 volcache_invalidate_ve (ve);
2356 return ENOENT;
2359 num_clones = 0;
2360 for (i = 0; i < min(NMAXNSERVERS,ve->entry.nServers); ++i) {
2361 u_long addr = htonl(ve->entry.serverNumber[i]);
2363 if (ve->entry.serverFlags[i] & bit
2364 && addr != 0
2365 && (ve->entry.serverFlags[i] & VLSF_DONTUSE) == 0) {
2366 ConnCacheEntry *conn;
2368 conn = conn_get (cell, addr, afsport,
2369 FS_SERVICE_ID, fs_probe, ce);
2370 if (!conn_isalivep (conn))
2371 conn->rtt = INT_MAX/2 ;
2372 else if (!volcache_reliablep_el(ve, i))
2373 conn->rtt = INT_MAX/4;
2374 else
2375 conn->rtt = rx_PeerOf(conn->connection)->srtt
2376 + rand() % RTT_FUZZ - RTT_FUZZ / 2;
2377 context->conns[num_clones].conn = conn;
2378 context->conns[num_clones].ve_ent = i;
2379 ++num_clones;
2383 if (num_clones == 0)
2384 return ENOENT;
2386 context->ve = ve;
2388 qsort (context->conns, num_clones, sizeof(*context->conns),
2389 fs_rtt_cmp);
2391 context->num_conns = num_clones;
2392 context->i = 0;
2394 return 0;
2398 * Find the first file server housing the volume for `e'.
2401 ConnCacheEntry *
2402 find_first_fs (fs_server_context *context)
2404 return find_next_fs (context, NULL, 0);
2408 * Initialize the file cache in `cachedir', with these values for high
2409 * and low-water marks.
2412 void
2413 fcache_init (u_long alowvnodes,
2414 u_long ahighvnodes,
2415 int64_t alowbytes,
2416 int64_t ahighbytes,
2417 uint64_t ablocksize,
2418 Bool recover)
2421 * Initialize all variables.
2424 int i;
2426 #if 0
2427 #ifdef KERBEROS
2428 fhopen_working = k_hasafs ();
2429 #else
2430 fhopen_working = 0;
2431 #endif
2432 #endif
2434 collectstats_init ();
2436 node_count = 0;
2437 lowvnodes = alowvnodes;
2438 highvnodes = ahighvnodes;
2439 lowbytes = alowbytes;
2440 highbytes = ahighbytes;
2441 highbytes = ahighbytes;
2442 blocksize = ablocksize;
2444 if (cache_dir == NULL)
2445 cache_dir = getcwd(NULL, 0);
2447 if (cache_dir == NULL)
2448 arla_errx (1, ADEBERROR, "fcache: getcwd failed");
2450 hashtab = hashtabnew (FCHASHSIZE, fcachecmp, fcachehash);
2451 if (hashtab == NULL)
2452 arla_errx (1, ADEBERROR, "fcache: hashtabnew failed");
2454 kernel_node_lru = listnew();
2455 node_lru = listnew();
2456 free_nodes = listnew();
2457 kernel_block_lru = listnew();
2458 block_lru = listnew();
2459 if (kernel_node_lru == NULL
2460 || node_lru == NULL
2461 || free_nodes == NULL
2462 || kernel_block_lru == NULL
2463 || block_lru == NULL)
2464 arla_errx (1, ADEBERROR, "fcache: listnew failed");
2466 invalid_heap = heap_new (ahighvnodes, expiration_time_cmp);
2467 if (invalid_heap == NULL)
2468 arla_errx (1, ADEBERROR, "fcache: heap_new failed");
2470 num_locks = num_workers + 8; /* a few for other threads */
2471 lockwaiters = malloc(num_locks * sizeof(*lockwaiters));
2472 if (lockwaiters == NULL)
2473 arla_errx(1, ADEBERROR, "fcache: malloc failed");
2475 for (i = 0; i < num_locks; i++)
2476 NNPQUEUE_INIT(&lockwaiters[i]);
2478 if (recover)
2479 fcache_recover_state ();
2481 if (LWP_CreateProcess (create_nodes, 0, 1, NULL, "fcache-create-nodes",
2482 &create_nodes_pid))
2483 arla_errx (1, ADEBERROR,
2484 "fcache: cannot create create-nodes thread");
2486 if (LWP_CreateProcess (cleaner, 0, 1, NULL, "fcache-cleaner",
2487 &cleaner_pid))
2488 arla_errx (1, ADEBERROR,
2489 "fcache: cannot create cleaner thread");
2491 if (LWP_CreateProcess (invalidator, 0, 1, NULL, "fcache-invalidator",
2492 &invalidator_pid))
2493 arla_errx (1, ADEBERROR,
2494 "fcache: cannot create invalidator thread");
2498 * set new values for those of lowvnodes, highvnodes that are not zero.
2499 * do some sanity checks
2500 * return 0 or an error code
2503 static int
2504 fcache_setvnodes(u_long alowvnodes,
2505 u_long ahighvnodes)
2507 int64_t high = highvnodes;
2508 int64_t low = lowvnodes;
2510 arla_warnx (ADEBFCACHE, "fcache_setvnodes");
2512 if (ahighvnodes != 0)
2513 high = ahighvnodes;
2515 if (alowvnodes != 0)
2516 low = alowvnodes;
2518 if (high < low)
2519 return EINVAL;
2521 if (high > highvnodes)
2522 LWP_NoYieldSignal (create_nodes);
2524 highvnodes = high;
2525 lowvnodes = low;
2527 return 0;
2531 * set new values for those of lowvnodes, highvnodes that are not zero.
2532 * do some sanity checks
2533 * return 0 or an error code
2536 static int
2537 fcache_setbytes(int64_t alowbytes,
2538 int64_t ahighbytes)
2540 int64_t high = highbytes;
2541 int64_t low = lowbytes;
2542 int64_t quotadiff;
2544 arla_warnx (ADEBFCACHE, "fcache_setbytes");
2546 if (alowbytes != 0)
2547 low = alowbytes;
2549 if (ahighbytes != 0)
2550 high = ahighbytes;
2552 if (high < low)
2553 return EINVAL;
2555 highbytes = high;
2556 lowbytes = low;
2558 quotadiff = fcache_set_appendquota();
2559 return install_appendquota(quotadiff);
2563 * set new high/low values for vnodes and bytes.
2564 * return 0 or an error code
2568 fcache_reinit(u_long alowvnodes,
2569 u_long ahighvnodes,
2570 int64_t alowbytes,
2571 int64_t ahighbytes)
2573 int error = fcache_setvnodes(alowvnodes, ahighvnodes);
2574 if (error)
2575 return error;
2577 return fcache_setbytes(alowbytes, ahighbytes);
2581 * Node has been touched, move to head of LRU list.
2584 void
2585 fcache_node_lru(FCacheEntry *e)
2587 if (e->flags.kernelp) {
2588 listdel(kernel_node_lru, e->lru_le);
2589 e->lru_le = listaddhead(kernel_node_lru, e);
2590 } else {
2591 listdel(node_lru, e->lru_le);
2592 e->lru_le = listaddhead(node_lru, e);
2595 assert(e->lru_le);
2599 * Block has been touched, move it to head of LRU list.
2602 void
2603 fcache_block_lru(struct block *b)
2605 if (b->flags.kernelp) {
2606 listdel(kernel_block_lru, b->lru_le);
2607 b->lru_le = listaddhead(kernel_block_lru, b);
2608 } else {
2609 listdel(block_lru, b->lru_le);
2610 b->lru_le = listaddhead(block_lru, b);
2613 assert(b->lru_le);
2615 /* fcache_node_lru(b->node); XXX hopefully happens anyway */
2618 static void
2619 data_unkernel_callback(struct block *block, void *data)
2621 if (!block->flags.kernelp)
2622 return;
2624 block->flags.kernelp = FALSE;
2625 listdel(kernel_block_lru, block->lru_le);
2626 block->lru_le = listaddhead(block_lru, block);
2628 assert(block->lru_le);
2632 * Node's kernelp is to be set to indicated value.
2634 * XXX kernelp implies things about attrused, dataused, etc which we
2635 * should take care of here, too... but what?
2638 void
2639 fcache_node_setkernelp(FCacheEntry *e, Bool val)
2641 assert(val == TRUE || val == FALSE);
2643 if (e->flags.kernelp == val)
2644 return;
2646 if (!val)
2647 block_foreach(e, data_unkernel_callback, NULL);
2649 e->flags.kernelp = val;
2651 if (val) {
2652 listdel(node_lru, e->lru_le);
2653 e->lru_le = listaddhead(kernel_node_lru, e);
2654 } else {
2655 listdel(kernel_node_lru, e->lru_le);
2656 e->lru_le = listaddhead(node_lru, e);
2659 assert(e->lru_le);
2663 * Set a block's busy flag to `val'.
2666 static void
2667 setbusy_block(struct block *b, Bool val)
2669 assert(b->flags.busy != val);
2670 b->flags.busy = val;
2674 * Set busy flag to `val' for a range of blocks, wake any waiters.
2677 void
2678 fcache_data_setbusy(FCacheEntry *e, uint64_t offset, uint64_t end,
2679 Bool val)
2681 uint64_t off;
2683 assert(block_offset(offset) == offset);
2685 for (off = offset; off < end; off += blocksize) {
2686 struct block *b = block_get(e, off);
2687 setbusy_block(b, val);
2689 wake_waiters(e);
2693 * Block's kernelp is to be set to indicated value.
2696 void
2697 fcache_data_setkernelp(FCacheEntry *e, uint64_t offset, Bool val, Bool unbusy)
2699 struct block *b;
2701 assert(block_offset(offset) == offset);
2703 AssertExclLocked(&e->lock);
2705 b = block_get(e, offset);
2706 if (!b) {
2707 assert(!val);
2708 assert(!unbusy);
2709 return;
2712 if (unbusy) {
2713 setbusy_block(b, FALSE);
2714 wake_waiters(e);
2717 if (b->flags.kernelp == val)
2718 return;
2720 b->flags.kernelp = val;
2722 if (val) {
2723 listdel(block_lru, b->lru_le);
2724 b->lru_le = listaddhead(kernel_block_lru, b);
2725 } else {
2726 listdel(kernel_block_lru, b->lru_le);
2727 b->lru_le = listaddhead(block_lru, b);
2730 assert(b->lru_le);
2734 * Find the entry for `fid' in the hash table.
2735 * If it's found, move it to the front of `node_lru' as well.
2738 static FCacheEntry *
2739 find_entry_nolock (VenusFid fid)
2741 FCacheEntry key;
2742 FCacheEntry *e;
2744 if (hashtab == NULL)
2745 return NULL;
2747 key.fid = fid;
2748 e = (FCacheEntry *)hashtabsearch (hashtab, (void *)&key);
2749 if (e != NULL)
2750 fcache_node_lru(e);
2752 return e;
2756 * Mark `e' as having `callback' and notify the kernel.
2757 * This might be overly harsh to opened files.
2760 static void
2761 stale (FCacheEntry *e, AFSCallBack callback)
2763 if (callback.CallBackType == CBDROPPED &&
2764 e->callback.CallBackType == CBDROPPED)
2765 return;
2767 if (fcache_islocked(e) || e->refcount > 0)
2768 e->flags.sentenced = TRUE;
2769 else {
2770 fcache_lock(e, FALSE);
2771 fcache_poller_unref(e);
2772 e->callback = callback;
2774 if (e->flags.kernelp)
2775 break_callback (e);
2776 else
2777 e->tokens = 0;
2779 if (e->status.FileType == TYPE_DIR)
2780 throw_data(e);
2781 fcache_unlock(e);
2785 struct stale_arg {
2786 VenusFid fid;
2787 AFSCallBack callback;
2791 * Iterate over all entries until we find an entry that matches in
2792 * only fid (without cell) and stale it.
2795 static Bool
2796 stale_unknown_cell (void *ptr, void *arg)
2798 FCacheEntry *e = (FCacheEntry *)ptr;
2799 struct stale_arg *sa = (struct stale_arg *)arg;
2801 if (e->fid.fid.Volume == sa->fid.fid.Volume
2802 && e->fid.fid.Vnode == sa->fid.fid.Vnode
2803 && e->fid.fid.Unique == sa->fid.fid.Unique)
2804 stale (e, sa->callback);
2806 return FALSE;
2810 * Call stale on the entry corresponding to `fid', if any.
2813 void
2814 fcache_stale_entry (VenusFid fid, AFSCallBack callback)
2816 FCacheEntry *e;
2818 if (fid.Cell == -1) {
2819 struct stale_arg arg;
2821 arg.fid = fid;
2822 arg.callback = callback;
2824 hashtabforeach (hashtab, stale_unknown_cell, &arg);
2825 return;
2828 e = find_entry_nolock (fid);
2829 if (e == NULL) {
2830 arla_warnx (ADEBFCACHE,
2831 "callback for non-existing file (%d, %u, %u, %u)",
2832 fid.Cell, fid.fid.Volume, fid.fid.Vnode, fid.fid.Unique);
2833 return;
2835 stale (e, callback);
2838 typedef struct {
2839 nnpfs_pag_t pag;
2840 int32_t cell;
2841 } fc_purgecred;
2844 * If ptr has cred arg, set it invalid
2847 static Bool
2848 purge_cred (void *ptr, void *arg)
2850 FCacheEntry *e = (FCacheEntry *)ptr;
2851 fc_purgecred *cred = (fc_purgecred *) arg;
2852 AccessEntry *ae = e->acccache;
2853 int i;
2855 if (e->fid.Cell == cred->cell || cred->cell == -1) {
2857 for(i = 0; i < NACCESS ; ++i)
2858 if(ae[i].cred == cred->pag) {
2859 ae[i].cred = ARLA_NO_AUTH_CRED;
2860 ae[i].access = ANONE;
2861 if (e->flags.kernelp)
2862 install_attr (e, FCACHE2NNPFSNODE_NO_LENGTH);
2863 break;
2866 return FALSE;
2871 * Mark cred as stale in kernel and all fcache-entries,
2872 * When cell == -1, flush all creds in this pag.
2875 void
2876 fcache_purge_cred (nnpfs_pag_t pag, int32_t cell)
2878 fc_purgecred cred;
2880 cred.pag = pag;
2881 cred.cell = cell;
2883 hashtabforeach(hashtab, purge_cred, &cred);
2887 * If ptr was retrieved from cell - volume , try to mark stale
2890 static Bool
2891 purge_volume (void *ptr, void *arg)
2893 FCacheEntry *e = (FCacheEntry *)ptr;
2894 VenusFid *fid = (VenusFid *) arg;
2896 if ((e->fid.Cell == fid->Cell || fid->Cell == -1)
2897 && e->fid.fid.Volume == fid->fid.Volume) {
2898 stale (e, broken_callback);
2900 return FALSE;
2904 * Mark all entries from cell.volume as stale
2907 void
2908 fcache_purge_volume (VenusFid fid)
2910 hashtabforeach(hashtab, purge_volume, &fid);
2914 * If `ptr' was retrieved from `host', mark it as stale.
2917 static Bool
2918 purge_host (void *ptr, void *arg)
2920 FCacheEntry *e = (FCacheEntry *)ptr;
2921 u_long *host = (u_long *)arg;
2923 assert (*host);
2924 if (e->host == *host)
2925 stale (e, broken_callback);
2926 return FALSE;
2930 * Mark all entries from the host `host' as stale.
2933 void
2934 fcache_purge_host (u_long host)
2936 hashtabforeach (hashtab, purge_host, &host);
2941 * If `ptr' is a mountpoint, mark it as stale.
2944 static Bool
2945 invalidate_mp (void *ptr, void *arg)
2947 FCacheEntry *e = (FCacheEntry *)ptr;
2948 if (e->flags.mountp)
2949 stale (e, broken_callback);
2950 return FALSE;
2954 * Invalidate all mountpoints to force them to be reread.
2957 void
2958 fcache_invalidate_mp (void)
2960 hashtabforeach (hashtab, invalidate_mp, NULL);
2964 * Mark `entry' as not being used.
2967 void
2968 fcache_unused (FCacheEntry *entry)
2970 AssertExclLocked(&entry->lock);
2972 assert(!entry->flags.appended || entry->flags.dirtied);
2973 assert(!entry->flags.kernelp);
2975 entry->flags.datausedp = entry->flags.attrusedp = FALSE;
2976 entry->tokens &= ~NNPFS_DATA_MASK;
2979 * we don't signal free_nodes here since we never
2980 * free the node (usedvnode--);
2983 /* throw stale and deleted-and-unreachable data */
2984 if (entry->flags.stale || entry->flags.dirtied || entry->flags.silly) {
2985 throw_data(entry); /* XXX overkill for volume callbacks etc */
2987 /* make sure we get fresh attrs. unnecessary for "stale" entries? */
2988 entry->flags.attrp = FALSE;
2993 * make up some status that might be valid for a mount-point
2996 static void
2997 fake_mp_status (FCacheEntry *e)
2999 AFSFetchStatus *status = &e->status;
3001 status->FileType = TYPE_DIR;
3002 status->LinkCount = 100;
3003 status->UnixModeBits = 0777;
3004 status->ClientModTime = 0;
3005 status->ServerModTime = 0;
3006 status->Owner = 0;
3007 status->Group = 0;
3011 * Return true if `entry' is a mountpoint
3014 static Bool
3015 mountpointp (FCacheEntry *entry)
3017 if (entry->status.FileType == TYPE_LINK
3018 && fcache_get_status_length(&entry->status) != 0
3019 && entry->status.UnixModeBits == 0644)
3020 return TRUE;
3021 return FALSE;
3025 * Mark `entry' as mountpoint or a fake mountpoint depending on
3026 * fake_mp is used or not.
3029 void
3030 fcache_mark_as_mountpoint (FCacheEntry *entry)
3032 if (fake_mp) {
3033 entry->flags.fake_mp = TRUE;
3034 fake_mp_status (entry);
3035 } else {
3036 entry->flags.mountp = TRUE;
3041 * Update all the relevant parts of `entry' after having received new
3042 * data from the file server.
3045 static void
3046 update_entry (FCacheEntry *entry,
3047 AFSFetchStatus *status,
3048 AFSCallBack *callback,
3049 AFSVolSync *volsync,
3050 ConnCacheEntry *conn,
3051 nnpfs_pag_t cred)
3053 struct timeval tv;
3054 AccessEntry *ae;
3055 unsigned long bitmask = 0141777; /* REG, DIR, STICKY, USR, GRP, OTH */
3057 if (entry->volume && cell_issuid_by_num (entry->volume->cell))
3058 bitmask |= 0006000; /* SUID, SGID */
3060 gettimeofday (&tv, NULL);
3062 entry->status = *status;
3063 entry->status.UnixModeBits &= bitmask;
3064 if (callback) {
3065 entry->callback = *callback;
3066 entry->callback.ExpirationTime += tv.tv_sec;
3067 add_to_invalidate (entry);
3069 if (volsync) {
3070 entry->volsync = *volsync;
3071 if (entry->volume)
3072 volcache_update_volsync (entry->volume, *volsync);
3075 if (conn) {
3076 fcache_poller_reref(entry, conn);
3077 entry->host = rx_HostOf(rx_PeerOf(conn->connection));
3078 } else {
3079 fcache_poller_unref(entry);
3080 entry->host = 0;
3083 entry->anonaccess = status->AnonymousAccess;
3084 findaccess (cred, entry->acccache, &ae);
3085 ae->cred = cred;
3086 ae->access = status->CallerAccess;
3087 if (!entry->flags.mountp && mountpointp (entry))
3088 fcache_mark_as_mountpoint (entry);
3092 * Update entry, common code for do_read_attr and get_attr_bulk
3095 static void
3096 update_attr_entry (FCacheEntry *entry,
3097 AFSFetchStatus *status,
3098 AFSCallBack *callback,
3099 AFSVolSync *volsync,
3100 ConnCacheEntry *conn,
3101 nnpfs_pag_t cred)
3103 if (block_any(entry) != BLOCK_NONE
3104 && entry->status.DataVersion != status->DataVersion) {
3106 if (entry->flags.datausedp) {
3107 /* we need to mark entry as stale, or we won't be able to
3108 * detect that once DataVersion has been updated
3111 /* actually, what we do need is to get rid of old
3112 * data. there are two ways to do that. for us to get rid
3113 * of all data we need kernel to drop the node. the other
3114 * way is to send gc messages until the all data is out of
3115 * kernel, but that's probably tricky to get right.
3118 entry->flags.stale = TRUE;
3119 arla_log(ADEBVLOG, "update_attr_entry(%d, %u, %u, %u): usage %llu",
3120 entry->fid.Cell, entry->fid.fid.Volume,
3121 entry->fid.fid.Vnode, entry->fid.fid.Unique,
3122 (unsigned long long)entry->usage);
3123 stale(entry, broken_callback);
3124 } else {
3125 entry->tokens &= ~NNPFS_DATA_MASK;
3126 throw_data(entry);
3130 update_entry (entry, status, callback, volsync,
3131 conn, cred);
3133 entry->tokens |= NNPFS_ATTR_R;
3134 entry->flags.attrp = TRUE;
3138 * We're about to update entry, if we have valid data we'll update
3139 * length ourselves when modifying the local cache. If data is stale,
3140 * throw it so we can get fresh.
3142 * This depends on
3143 * 1. Following code to do the actual update if all is well
3144 * 2. Following code to get new data if we have stale data
3145 * (maybe we should do that here)
3148 static void
3149 update_modify_dir(FCacheEntry *entry,
3150 AFSFetchStatus *status,
3151 AFSCallBack *callback,
3152 AFSVolSync *volsync,
3153 ConnCacheEntry *conn,
3154 nnpfs_pag_t cred)
3156 entry->status.DataVersion++;
3158 if (block_any(entry) != BLOCK_NONE
3159 && entry->status.DataVersion != status->DataVersion)
3161 arla_warnx(ADEBWARN, "DataVersion mismatch, refreshing directory");
3163 throw_data(entry); /* XXX installed in kernel, but should be locked */
3164 entry->tokens &= ~NNPFS_DATA_MASK;
3166 /* XXX entry->flags.datausedp = FALSE; perhaps? */
3168 /* keep server's length so we know how much data to fetch */
3169 } else {
3170 uint64_t len = fcache_get_status_length(&entry->status);
3171 fcache_set_status_length(status, len);
3174 update_entry(entry, status, callback, volsync, conn, cred);
3179 * Give up all callbacks.
3182 static int
3183 giveup_all_callbacks (uint32_t cell, uint32_t host, void *arg)
3185 CredCacheEntry *ce;
3186 ConnCacheEntry *conn;
3187 int ret;
3189 ce = cred_get (cell, 0, CRED_ANY);
3190 assert (ce != NULL);
3192 conn = conn_get (cell, host, afsport, FS_SERVICE_ID, fs_probe, ce);
3193 cred_free (ce);
3195 if (conn_isalivep (conn)) {
3197 ret = RXAFS_GiveUpAllCallBacks(conn->connection);
3198 if (ret != 0 && ret != RXGEN_OPCODE) {
3199 struct in_addr in_addr;
3201 in_addr.s_addr = rx_HostOf(rx_PeerOf(conn->connection));
3202 arla_warn (ADEBWARN, ret, "GiveUpAllCallBacks %s",
3203 inet_ntoa (in_addr));
3204 if (host_downp(ret)) {
3205 conn_dead (conn);
3206 ret = ENETDOWN;
3211 conn_free (conn);
3213 return 0;
3217 fcache_giveup_all_callbacks (void)
3219 Listitem *item;
3221 poller_foreach(giveup_all_callbacks, NULL);
3223 for (item = listtail(node_lru);
3224 item != NULL;
3225 item = listprev(node_lru, item)) {
3226 FCacheEntry *entry = (FCacheEntry *)listdata(item);
3228 if (entry->flags.attrp &&
3229 entry->flags.silly == FALSE &&
3230 entry->host != 0) {
3232 CredCacheEntry *ce;
3233 ConnCacheEntry *conn;
3234 AFSCBFids fids;
3235 AFSCBs cbs;
3236 int ret;
3238 ce = cred_get (entry->fid.Cell, 0, CRED_ANY);
3239 assert (ce != NULL);
3241 conn = conn_get (entry->fid.Cell, entry->host, afsport,
3242 FS_SERVICE_ID, fs_probe, ce);
3243 cred_free (ce);
3245 fids.len = cbs.len = 1;
3246 fids.val = &entry->fid.fid;
3247 cbs.val = &entry->callback;
3249 if (conn_isalivep (conn)) {
3250 ret = RXAFS_GiveUpCallBacks (conn->connection, &fids, &cbs);
3251 if (ret) {
3252 struct in_addr in_addr;
3254 in_addr.s_addr = rx_HostOf(rx_PeerOf(conn->connection));
3255 arla_warn (ADEBFCACHE, ret, "RXAFS_GiveUpCallBacks %s",
3256 inet_ntoa (in_addr));
3259 conn_free (conn);
3262 return 0; /* XXX */
3266 * discard all cached attrs to force revalidation of entries
3267 * intended for reconnect after disconnected mode.
3270 void
3271 fcache_discard_attrs(void)
3273 Listitem *item;
3275 for (item = listtail(node_lru);
3276 item != NULL;
3277 item = listprev(node_lru, item)) {
3278 FCacheEntry *entry = (FCacheEntry *)listdata(item);
3280 if (entry->flags.attrp &&
3281 entry->flags.silly == FALSE)
3282 entry->flags.attrp = FALSE;
3287 * Obtain new callbacks for all entries in the cache.
3291 fcache_reobtain_callbacks (struct nnpfs_cred *cred)
3293 Listitem *item;
3294 int ret;
3296 for (item = listtail(node_lru);
3297 item != NULL;
3298 item = listprev(node_lru, item)) {
3299 FCacheEntry *entry = (FCacheEntry *)listdata(item);
3301 fcache_lock(entry, FALSE);
3302 if (entry->flags.usedp &&
3303 entry->flags.silly == FALSE &&
3304 entry->host != 0) {
3306 CredCacheEntry *ce;
3307 ConnCacheEntry *conn;
3308 AFSFetchStatus status;
3309 AFSCallBack callback;
3310 AFSVolSync volsync;
3311 VolCacheEntry *vol;
3313 ce = cred_get (entry->fid.Cell, cred->pag, CRED_ANY);
3314 assert (ce != NULL);
3316 conn = conn_get (entry->fid.Cell, entry->host, afsport,
3317 FS_SERVICE_ID, fs_probe, ce);
3318 if (!conn_isalivep(conn))
3319 goto out;
3321 * does this belong here?
3324 ret = volcache_getbyid (entry->fid.fid.Volume,
3325 entry->fid.Cell, ce, &vol, NULL);
3326 if (ret == 0)
3327 entry->volume = vol;
3329 ret = RXAFS_FetchStatus (conn->connection,
3330 &entry->fid.fid,
3331 &status,
3332 &callback,
3333 &volsync);
3334 if (ret)
3335 arla_warn (ADEBFCACHE, ret, "RXAFS_FetchStatus");
3336 else {
3337 update_attr_entry (entry, &status, &callback, &volsync,
3338 conn, ce->cred);
3339 if (entry->flags.kernelp)
3340 break_callback (entry);
3342 fcache_counter.fetch_attr++;
3343 out:
3344 if (conn)
3345 conn_free (conn);
3346 cred_free (ce);
3348 fcache_unlock(entry);
3350 return 0; /* XXX */
3354 * Return true iff there's any point in trying the next fs.
3357 static Bool
3358 try_next_fs (int error, const VenusFid *fid)
3360 switch (error) {
3361 #ifdef KERBEROS
3362 case RXKADUNKNOWNKEY:
3363 #endif
3364 case ARLA_CALL_DEAD :
3365 case ARLA_INVALID_OPERATION :
3366 case ARLA_CALL_TIMEOUT :
3367 case ARLA_EOF :
3368 case ARLA_PROTOCOL_ERROR :
3369 case ARLA_USER_ABORT :
3370 case ARLA_ADDRINUSE :
3371 case ARLA_MSGSIZE :
3372 case ARLA_VSALVAGE :
3373 case ARLA_VNOSERVICE :
3374 case ARLA_VOFFLINE :
3375 case ARLA_VBUSY :
3376 case ARLA_VIO :
3377 return TRUE;
3378 case ARLA_VNOVOL :
3379 case ARLA_VMOVED :
3380 if (fid && !volcache_reliablep (fid->fid.Volume, fid->Cell))
3381 volcache_invalidate (fid->fid.Volume, fid->Cell);
3382 return TRUE;
3383 case 0 :
3384 return FALSE;
3385 default :
3386 return FALSE;
3391 * Fetch the attributes for the file in `entry' from the file_server,
3392 * using the credentials in `ce' and returning the connection in
3393 * `ret_conn'
3395 * `entry' must be write-locked.
3397 * If an error code is returned `fs_server_context' is already freed.
3398 * If everything is ok, `fs_server_context' must be freed by the caller.
3401 static int
3402 do_read_attr (FCacheEntry *entry,
3403 CredCacheEntry *ce,
3404 ConnCacheEntry **ret_conn,
3405 fs_server_context *ret_context)
3407 ConnCacheEntry *conn;
3408 AFSFetchStatus status;
3409 AFSCallBack callback;
3410 AFSVolSync volsync;
3411 struct collect_stat collectstat;
3412 int ret;
3414 AssertExclLocked(&entry->lock);
3416 *ret_conn = NULL;
3418 ret = init_fs_context(entry, ce, ret_context);
3419 if (ret)
3420 return ret;
3422 for (conn = find_first_fs (ret_context);
3423 conn != NULL;
3424 conn = find_next_fs (ret_context, conn, ret)) {
3426 collectstats_start(&collectstat);
3427 ret = RXAFS_FetchStatus (conn->connection,
3428 &entry->fid.fid,
3429 &status,
3430 &callback,
3431 &volsync);
3432 collectstats_stop(&collectstat, entry, conn,
3433 find_partition(ret_context),
3434 arla_STATISTICS_REQTYPE_FETCHSTATUS, 1);
3435 arla_warnx (ADEBFCACHE, "trying to fetch status: %d", ret);
3436 if (!try_next_fs (ret, &entry->fid))
3437 break;
3439 if (ret) {
3440 arla_warn (ADEBFCACHE, ret, "fetch-status");
3441 if (host_downp(ret))
3442 ret = ENETDOWN;
3443 free_fs_server_context (ret_context);
3444 return ret;
3447 fcache_counter.fetch_attr++;
3449 update_attr_entry (entry, &status, &callback, &volsync,
3450 conn, ce->cred);
3452 AssertExclLocked(&entry->lock);
3454 *ret_conn = conn;
3455 return 0;
3460 * Read the attributes of `entry' from the file server and store them.
3461 * `e' must be write-locked.
3465 read_attr (FCacheEntry *entry, CredCacheEntry *ce)
3467 int ret;
3468 ConnCacheEntry *conn;
3469 fs_server_context context;
3471 AssertExclLocked(&entry->lock);
3473 init_fs_server_context (&context);
3474 ret = do_read_attr (entry, ce, &conn, &context);
3475 if (ret)
3476 return ret;
3477 free_fs_server_context (&context);
3478 return 0;
3482 * Read the contents of `entry' from the file server and store it.
3484 * If the wanted data is busy, try waiting for it.
3487 static int
3488 read_data (FCacheEntry *entry, ConnCacheEntry *conn, CredCacheEntry *ce,
3489 long partition, uint64_t wanted_offset, uint64_t wanted_end)
3491 struct rx_call *call;
3492 int ret = 0;
3493 int nblocks;
3494 uint64_t end, nbytes = 0;
3495 int64_t sizefs;
3496 AFSFetchStatus status;
3497 AFSCallBack callback;
3498 AFSVolSync volsync;
3499 struct collect_stat collectstat;
3500 uint32_t sizefs4;
3501 uint64_t offset;
3502 BlockState state;
3503 Bool unlocked = FALSE;
3505 arla_warnx (ADEBMISC, "read_data");
3507 AssertExclLocked(&entry->lock);
3509 if (connected_mode == DISCONNECTED)
3510 return ENETDOWN;
3512 state = first_wanted_range(entry, wanted_offset, wanted_end,
3513 &offset, &end);
3514 switch (state) {
3515 case BLOCK_GOT: /* got block to fetch, process below */
3516 break;
3517 case BLOCK_BUSY: /* already fetching block, wait for them and retry */
3518 wait_busy(entry);
3519 return 0;
3520 case BLOCK_NONE:
3521 return 0; /* already cached, no blocks to fetch */
3524 /* figure out how much more then we need we want to fetch */
3525 /* XXX end = stats_fetch_round(conn, partition, end); */
3526 if (end > fcache_get_status_length(&entry->status))
3527 end = fcache_get_status_length(&entry->status);
3529 nbytes = end - offset;
3531 nblocks = create_busy_blocks(entry, offset, end);
3532 if (nblocks < 0) {
3533 ret = errno;
3534 arla_warn(ADEBFCACHE, ret, "read_data blocks");
3535 delete_unbusy_blocks(entry, offset, offset, end, FALSE);
3536 return ret;
3540 * Release lock on ordinary nodes during rpc, it's still
3541 * referenced.
3544 fcache_unlock(entry);
3545 unlocked = TRUE;
3547 ret = fcache_update_usage(entry, nblocks);
3548 if (ret) {
3549 arla_warn(ADEBFCACHE, ret, "read_data usage");
3550 fcache_lock(entry, FALSE);
3551 delete_unbusy_blocks(entry, offset, offset, end, FALSE);
3552 return ret;
3555 again:
3556 /* now go talk to the world */
3557 call = rx_NewCall (conn->connection);
3558 if (call == NULL) {
3559 arla_warnx (ADEBMISC, "rx_NewCall failed");
3560 ret = ENOMEM;
3561 goto out;
3564 arla_warnx(ADEBFCACHE, "read_data: from %#llx to %#llx",
3565 offset, end);
3567 collectstats_start(&collectstat);
3568 if (conn_get_fs_support64(conn)) {
3569 ret = StartRXAFS_FetchData64 (call,
3570 &entry->fid.fid,
3571 offset,
3572 nbytes);
3573 if (ret == RXGEN_OPCODE) {
3574 rx_EndCall(call,ret);
3575 conn_set_fs_support64(conn, FALSE);
3576 goto again;
3578 } else if (end >> 32)
3579 ret = EFBIG;
3580 else
3581 ret = StartRXAFS_FetchData (call,
3582 &entry->fid.fid,
3583 offset,
3584 nbytes);
3586 if(ret) {
3587 arla_warn (ADEBFCACHE, ret, "fetch-data");
3588 rx_EndCall(call,ret);
3589 goto out;
3592 if (conn_get_fs_support64(conn)) {
3593 ret = rx_Read (call, &sizefs4, sizeof(sizefs4));
3594 if (ret != sizeof(sizefs4)) {
3595 ret = rx_GetCallError(call);
3596 if (ret == RXGEN_OPCODE && conn_get_fs_support64(conn)) {
3597 rx_EndCall(call,0);
3598 conn_set_fs_support64(conn, FALSE);
3599 goto again;
3601 ret = conv_to_arla_errno(ret);
3602 arla_warn (ADEBFCACHE, ret, "Error reading length");
3603 rx_EndCall(call, 0);
3604 goto out;
3606 sizefs = (int64_t)ntohl(sizefs4) << 32;
3607 } else
3608 sizefs = 0;
3610 ret = rx_Read (call, &sizefs4, sizeof(sizefs4));
3611 if (ret != sizeof(sizefs4)) {
3612 ret = conv_to_arla_errno(rx_GetCallError(call));
3613 arla_warn (ADEBFCACHE, ret, "Error reading length");
3614 rx_EndCall(call, 0);
3615 goto out;
3618 sizefs |= ntohl (sizefs4);
3619 if (sizefs < 0)
3620 sizefs = 0;
3622 /* get node lock again, now that we're about to change things */
3623 fcache_lock(entry, FALSE);
3624 unlocked = FALSE;
3626 ret = copyrx2cache(call, entry, offset, sizefs);
3627 if (ret) {
3628 arla_warn (ADEBFCACHE, ret, "copyrx2cache");
3629 rx_EndCall(call, ret);
3630 goto out;
3633 if (conn_get_fs_support64(conn)) {
3634 ret = EndRXAFS_FetchData64 (call,
3635 &status,
3636 &callback,
3637 &volsync);
3638 } else {
3639 ret = EndRXAFS_FetchData (call,
3640 &status,
3641 &callback,
3642 &volsync);
3644 if (ret)
3645 arla_warn (ADEBWARN, ret, "EndRXAFS_FetchData");
3646 ret = rx_EndCall (call, ret);
3647 if (ret) {
3648 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
3649 goto out;
3651 collectstats_stop(&collectstat, entry, conn,
3652 partition, arla_STATISTICS_REQTYPE_FETCHDATA, sizefs);
3654 fcache_counter.fetch_data++;
3656 update_entry (entry, &status, &callback, &volsync, conn, ce->cred);
3658 out:
3659 if (unlocked)
3660 fcache_lock(entry, FALSE);
3663 * Unbusy blocks. If we didn't get all created blocks, the rest
3664 * should be removed.
3666 if (ret)
3667 sizefs = 0;
3668 delete_unbusy_blocks(entry, offset,
3669 block_next_offset(offset + sizefs), end,
3670 TRUE);
3672 AssertExclLocked(&entry->lock);
3674 return ret;
3678 * Write the contents of the cache file back to the file server.
3682 write_data(FCacheEntry *entry, FCacheEntry *data_entry,
3683 uint64_t offset, uint64_t length,
3684 AFSStoreStatus *storestatus, CredCacheEntry *ce)
3686 FCacheEntry *fd_entry = entry;
3687 ConnCacheEntry *conn;
3688 struct rx_call *call;
3689 int ret;
3690 uint64_t sizefs;
3691 AFSFetchStatus status;
3692 AFSVolSync volsync;
3693 fs_server_context context;
3694 struct collect_stat collectstat;
3696 AssertExclLocked(&entry->lock);
3698 if (data_entry) {
3699 AssertExclLocked(&data_entry->lock);
3700 fd_entry = data_entry;
3703 if (connected_mode != CONNECTED || entry->flags.silly)
3704 return 0;
3706 sizefs = fcache_get_status_length(&fd_entry->status);
3708 /* avoid gc */
3709 fcache_data_setbusy(fd_entry, offset, offset + length, TRUE);
3711 /* keep node lock for now, perhaps unnecessary */
3713 ret = init_fs_context(entry, ce, &context);
3714 if (ret) {
3715 fcache_data_setbusy(fd_entry, offset, offset + length, FALSE);
3716 return ret;
3719 for (conn = find_first_fs (&context);
3720 conn != NULL;
3721 conn = find_next_fs (&context, conn, ret)) {
3723 again:
3724 call = rx_NewCall (conn->connection);
3725 if (call == NULL) {
3726 arla_warnx (ADEBMISC, "rx_NewCall failed");
3727 ret = ENOMEM;
3728 break;
3731 collectstats_start(&collectstat);
3732 if (conn_get_fs_support64(conn)) {
3733 ret = StartRXAFS_StoreData64 (call, &entry->fid.fid,
3734 storestatus,
3735 offset,
3736 length,
3737 sizefs);
3738 if (ret == RXGEN_OPCODE) {
3739 rx_EndCall(call,ret);
3740 conn_set_fs_support64(conn, FALSE);
3741 goto again;
3743 } else if ((uint64_t)sizefs >> 32) {
3744 ret = EFBIG;
3745 } else {
3746 ret = StartRXAFS_StoreData (call, &entry->fid.fid,
3747 storestatus,
3748 offset,
3749 length,
3750 sizefs);
3753 if (host_downp(ret)) {
3754 rx_EndCall(call, ret);
3755 continue;
3756 } else if (ret) {
3757 arla_warn (ADEBFCACHE, ret, "store-data");
3758 rx_EndCall(call, 0);
3759 break;
3762 ret = copycache2rx(fd_entry, call, offset, length);
3763 if (ret == RXGEN_OPCODE && conn_get_fs_support64(conn)) {
3764 rx_EndCall(call,ret);
3765 conn_set_fs_support64(conn, FALSE);
3766 goto again;
3767 } else if (ret) {
3768 rx_EndCall(call, ret);
3769 arla_warn (ADEBFCACHE, ret, "copycache2rx");
3770 break;
3773 if (conn_get_fs_support64(conn)) {
3774 ret = EndRXAFS_StoreData64 (call,
3775 &status,
3776 &volsync);
3777 if (ret == RXGEN_OPCODE) {
3778 rx_EndCall(call, 0);
3779 conn_set_fs_support64(conn, FALSE);
3780 goto again;
3782 } else {
3783 ret = EndRXAFS_StoreData (call,
3784 &status,
3785 &volsync);
3787 if (ret) {
3788 rx_EndCall (call, ret);
3789 arla_warnx (ADEBFCACHE, "EndRXAFS_StoreData");
3790 break;
3793 ret = rx_EndCall (call, 0);
3794 if (ret) {
3795 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
3797 collectstats_stop(&collectstat, entry, conn,
3798 find_partition(&context),
3799 arla_STATISTICS_REQTYPE_STOREDATA, sizefs);
3800 break;
3803 if (conn != NULL) {
3804 if (ret == 0) {
3805 fcache_counter.store_data++;
3806 update_entry (entry, &status, NULL, &volsync,
3807 conn, ce->cred);
3808 } else {
3809 #if 0
3811 * We can't do this, it will corrupt the cache since nnpfs
3812 * will still think it have the data, and then when we
3813 * write back the file to the fileserver, it will be
3814 * filled with zeros. Happens if you are unlucky so store
3815 * a file at the same moment as your credentials expire.
3817 ftruncate (fd, 0);
3818 usedbytes -= entry->usage;
3819 entry->usage = 0;
3820 #endif
3823 fcache_data_setbusy(fd_entry, offset, offset + length, FALSE);
3825 if (host_downp(ret))
3826 ret = ENETDOWN;
3828 free_fs_server_context (&context);
3829 AssertExclLocked(&entry->lock);
3830 if (data_entry) {
3831 AssertExclLocked(&data_entry->lock);
3833 /* make sure data is up to date, copy it or just throw */
3834 throw_data(entry);
3837 return ret;
3841 * Truncate the file in `entry' to `size' bytes.
3845 truncate_file (FCacheEntry *entry, uint64_t size,
3846 AFSStoreStatus *storestatus, CredCacheEntry *ce)
3848 fs_server_context context;
3849 ConnCacheEntry *conn;
3850 struct rx_call *call;
3851 AFSFetchStatus status;
3852 AFSVolSync volsync;
3853 int ret;
3855 AssertExclLocked(&entry->lock);
3857 if (connected_mode != CONNECTED)
3858 return 0;
3860 /* XXX needed? */
3861 if (size) {
3862 ret = fcache_verify_data(entry, ce, 0, 0);
3863 if (ret)
3864 return ret;
3867 ret = init_fs_context(entry, ce, &context);
3868 if (ret)
3869 return ret;
3871 for (conn = find_first_fs (&context);
3872 conn != NULL;
3873 conn = find_next_fs (&context, conn, ret)) {
3875 again:
3876 call = rx_NewCall (conn->connection);
3877 if (call == NULL) {
3878 arla_warnx (ADEBMISC, "rx_NewCall failed");
3879 ret = ENOMEM;
3880 break;
3883 if (conn_get_fs_support64(conn)) {
3884 ret = StartRXAFS_StoreData64 (call,
3885 &entry->fid.fid,
3886 storestatus,
3887 size,
3889 size);
3890 if (ret == RXGEN_OPCODE) {
3891 rx_EndCall(call, ret);
3892 conn_set_fs_support64(conn, FALSE);
3893 goto again;
3895 } else if (size >> 32)
3896 ret = EFBIG;
3897 else
3898 ret = StartRXAFS_StoreData (call,
3899 &entry->fid.fid,
3900 storestatus,
3901 size,
3903 size);
3904 if (host_downp(ret)) {
3905 rx_EndCall(call, ret);
3906 continue;
3907 } else if(ret) {
3908 arla_warn (ADEBFCACHE, ret, "store-data");
3909 rx_EndCall(call, 0);
3910 break;
3913 if (conn_get_fs_support64(conn)) {
3914 ret = EndRXAFS_StoreData64 (call,
3915 &status,
3916 &volsync);
3917 if (ret == RXGEN_OPCODE) {
3918 rx_EndCall(call, 0);
3919 conn_set_fs_support64(conn, FALSE);
3920 goto again;
3922 } else {
3923 ret = EndRXAFS_StoreData (call,
3924 &status,
3925 &volsync);
3927 if (ret) {
3928 rx_EndCall (call, ret);
3929 arla_warnx (ADEBFCACHE, "EndRXAFS_StoreData");
3930 break;
3933 ret = rx_EndCall (call, 0);
3934 if (ret)
3935 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
3937 break;
3940 if (ret == 0) {
3941 ret = abuf_truncate(entry, size);
3942 if (ret) {
3943 arla_warn (ADEBFCACHE, ret, "abuf_truncate %ld", (long)size);
3944 free_fs_server_context (&context);
3945 return ret;
3948 fcache_counter.store_data++;
3949 update_entry (entry, &status, NULL, &volsync,
3950 conn, ce->cred);
3953 free_fs_server_context (&context);
3955 if (host_downp(ret))
3956 ret = ENETDOWN;
3958 AssertExclLocked(&entry->lock);
3959 return ret;
3963 * Set the attributes of the file in `entry' to `status'.
3967 write_attr (FCacheEntry *entry,
3968 const AFSStoreStatus *store_status,
3969 CredCacheEntry *ce)
3971 ConnCacheEntry *conn = NULL;
3972 int ret;
3973 AFSFetchStatus status;
3974 AFSVolSync volsync;
3976 AssertExclLocked(&entry->lock);
3978 /* Don't write attributes to deleted files */
3979 if (entry->flags.silly)
3980 return 0;
3982 if (connected_mode == CONNECTED) {
3983 fs_server_context context;
3984 struct collect_stat collectstat;
3986 ret = init_fs_context(entry, ce, &context);
3987 if (ret)
3988 return ret;
3990 for (conn = find_first_fs (&context);
3991 conn != NULL;
3992 conn = find_next_fs (&context, conn, ret)) {
3994 collectstats_start(&collectstat);
3995 ret = RXAFS_StoreStatus (conn->connection,
3996 &entry->fid.fid,
3997 store_status,
3998 &status,
3999 &volsync);
4000 if (host_downp(ret)) {
4001 continue;
4002 } else if (ret) {
4003 arla_warn (ADEBFCACHE, ret, "store-status");
4004 free_fs_server_context (&context);
4005 conn = NULL;
4006 goto out;
4008 conn_ref(conn);
4009 break;
4012 if (ret == 0)
4013 collectstats_stop(&collectstat, entry, conn,
4014 find_partition(&context),
4015 arla_STATISTICS_REQTYPE_STORESTATUS, 1);
4018 free_fs_server_context (&context);
4020 if (host_downp(ret)) {
4021 ret = ENETDOWN;
4022 goto out;
4024 update_entry (entry, &status, NULL, &volsync, conn, ce->cred);
4026 } else {
4027 assert (conn == NULL);
4029 fcache_counter.store_attr++;
4030 if (store_status->Mask & SS_MODTIME) {
4031 entry->status.ClientModTime = store_status->ClientModTime;
4032 entry->status.ServerModTime = store_status->ClientModTime;
4034 if (store_status->Mask & SS_OWNER)
4035 entry->status.Owner = store_status->Owner;
4036 if (store_status->Mask & SS_GROUP)
4037 entry->status.Group = store_status->Group;
4038 if (store_status->Mask & SS_MODEBITS)
4039 entry->status.UnixModeBits = store_status->UnixModeBits;
4040 ret = 0;
4043 out:
4044 if (conn)
4045 conn_free(conn);
4046 AssertExclLocked(&entry->lock);
4048 return ret;
4052 * Create a file. The new node is returned locked in `ret_entry'.
4056 create_file (FCacheEntry *dir_entry,
4057 const char *name, AFSStoreStatus *store_attr,
4058 FCacheEntry **ret_entry, CredCacheEntry *ce)
4060 ConnCacheEntry *conn = NULL;
4061 int ret;
4062 FCacheEntry *child_entry;
4063 AFSFetchStatus fetch_attr;
4064 VenusFid child_fid;
4065 AFSFetchStatus status;
4066 AFSCallBack callback;
4067 AFSVolSync volsync;
4069 AssertExclLocked(&dir_entry->lock);
4071 *ret_entry = NULL;
4073 if (connected_mode == CONNECTED) {
4074 fs_server_context context;
4076 ret = init_fs_context(dir_entry, ce, &context);
4077 if (ret)
4078 return ret;
4080 for (conn = find_first_fs (&context);
4081 conn != NULL;
4082 conn = find_next_fs (&context, conn, ret)) {
4084 ret = RXAFS_CreateFile (conn->connection,
4085 &dir_entry->fid.fid,
4086 name,
4087 store_attr,
4088 &child_fid.fid,
4089 &fetch_attr,
4090 &status,
4091 &callback,
4092 &volsync);
4093 if (host_downp(ret)) {
4094 continue;
4095 } else if (ret) {
4096 free_fs_server_context (&context);
4097 arla_warn (ADEBFCACHE, ret, "CreateFile");
4098 conn = NULL;
4099 goto out;
4101 conn_ref(conn);
4102 break;
4105 free_fs_server_context (&context);
4107 if (host_downp(ret)) {
4108 ret = ENETDOWN;
4109 goto out;
4112 fetch_attr.CallerAccess |= AADMIN;
4114 update_modify_dir(dir_entry, &status, &callback,
4115 &volsync, conn, ce->cred);
4116 } else {
4117 static int fakefid = 1001;
4119 assert(conn == NULL);
4121 ret = 0;
4123 child_fid.fid.Volume = dir_entry->fid.fid.Volume;
4124 child_fid.fid.Vnode = fakefid;
4125 child_fid.fid.Unique = fakefid;
4126 fakefid += 2;
4128 fetch_attr.InterfaceVersion = 1;
4129 fetch_attr.FileType = TYPE_FILE;
4130 fetch_attr.LinkCount = 1;
4131 fetch_attr.Length = 0;
4132 fetch_attr.DataVersion = 1;
4133 fetch_attr.Author = store_attr->Owner;
4134 fetch_attr.Owner = store_attr->Owner;
4135 fetch_attr.CallerAccess = dir_entry->status.CallerAccess;
4136 fetch_attr.AnonymousAccess = dir_entry->status.AnonymousAccess;
4137 fetch_attr.UnixModeBits = store_attr->UnixModeBits;
4138 fetch_attr.ParentVnode = dir_entry->fid.fid.Vnode;
4139 fetch_attr.ParentUnique = dir_entry->fid.fid.Unique;
4140 fetch_attr.ResidencyMask = 1;
4141 fetch_attr.ClientModTime = store_attr->ClientModTime;
4142 fetch_attr.ServerModTime = store_attr->ClientModTime;
4143 fetch_attr.Group = store_attr->Group;
4144 fetch_attr.SyncCounter = 0;
4145 fetch_attr.DataVersionHigh = 0;
4146 fetch_attr.LockCount = 0;
4147 fetch_attr.LengthHigh = 0;
4148 fetch_attr.ErrorCode = 0;
4151 child_fid.Cell = dir_entry->fid.Cell;
4153 ret = fcache_get(&child_entry, child_fid, ce);
4154 if (ret) {
4155 arla_warn (ADEBFCACHE, ret, "fcache_get");
4156 goto out;
4159 update_entry(child_entry, &fetch_attr, NULL, NULL,
4160 conn, ce->cred);
4162 throw_data(child_entry);
4163 ret = create_block(child_entry, 0);
4164 if (ret) {
4165 arla_warn(ADEBFCACHE, ret, "create cache file %u",
4166 (unsigned)child_entry->index);
4167 fcache_release(child_entry);
4168 goto out;
4171 child_entry->tokens |= NNPFS_ATTR_R | NNPFS_DATA_R | NNPFS_DATA_W;
4172 child_entry->flags.attrp = TRUE;
4174 *ret_entry = child_entry;
4176 out:
4177 if (conn)
4178 conn_free(conn);
4180 AssertExclLocked(&dir_entry->lock);
4182 return ret;
4186 * Create a directory.
4190 create_directory (FCacheEntry *dir_entry,
4191 const char *name, AFSStoreStatus *store_attr,
4192 VenusFid *child_fid, AFSFetchStatus *fetch_attr,
4193 CredCacheEntry *ce)
4195 ConnCacheEntry *conn = NULL;
4196 int ret;
4197 AFSFid OutFid;
4198 FCacheEntry *child_entry;
4199 AFSFetchStatus status;
4200 AFSCallBack callback;
4201 AFSVolSync volsync;
4204 AssertExclLocked(&dir_entry->lock);
4206 if (connected_mode == CONNECTED) {
4207 fs_server_context context;
4209 ret = init_fs_context(dir_entry, ce, &context);
4210 if (ret)
4211 return ret;
4213 for (conn = find_first_fs (&context);
4214 conn != NULL;
4215 conn = find_next_fs (&context, conn, ret)) {
4217 ret = RXAFS_MakeDir (conn->connection,
4218 &dir_entry->fid.fid,
4219 name,
4220 store_attr,
4221 &OutFid,
4222 fetch_attr,
4223 &status,
4224 &callback,
4225 &volsync);
4227 if (host_downp(ret)) {
4228 continue;
4229 } else if (ret) {
4230 free_fs_server_context (&context);
4231 arla_warn (ADEBFCACHE, ret, "MakeDir");
4232 conn = NULL;
4233 goto out;
4235 conn_ref(conn);
4236 break;
4238 free_fs_server_context (&context);
4240 if (host_downp(ret)) {
4241 ret = ENETDOWN;
4242 goto out;
4245 update_modify_dir(dir_entry, &status, &callback, &volsync,
4246 conn, ce->cred);
4247 } else {
4248 static int fakedir = 1000;
4250 ret = 0;
4252 assert(conn == NULL);
4254 OutFid.Volume = dir_entry->fid.fid.Volume;
4255 OutFid.Vnode = fakedir;
4256 OutFid.Unique = fakedir;
4257 fakedir += 2;
4259 fetch_attr->InterfaceVersion = 1;
4260 fetch_attr->FileType = TYPE_DIR;
4261 fetch_attr->LinkCount = 2;
4262 fetch_attr->Length = AFSDIR_PAGESIZE;
4263 fetch_attr->DataVersion = 1;
4264 fetch_attr->Author = store_attr->Owner;
4265 fetch_attr->Owner = store_attr->Owner;
4266 fetch_attr->CallerAccess = dir_entry->status.CallerAccess;
4267 fetch_attr->AnonymousAccess = dir_entry->status.AnonymousAccess;
4268 fetch_attr->UnixModeBits = store_attr->UnixModeBits;
4269 fetch_attr->ParentVnode = dir_entry->fid.fid.Vnode;
4270 fetch_attr->ParentUnique = dir_entry->fid.fid.Unique;
4271 fetch_attr->ResidencyMask = 1;
4272 fetch_attr->ClientModTime = store_attr->ClientModTime;
4273 fetch_attr->ServerModTime = store_attr->ClientModTime;
4274 fetch_attr->Group = store_attr->Group;
4275 fetch_attr->SyncCounter = 0;
4276 fetch_attr->DataVersionHigh = 0;
4277 fetch_attr->LockCount = 0;
4278 fetch_attr->LengthHigh = 0;
4279 fetch_attr->ErrorCode = 0;
4282 child_fid->Cell = dir_entry->fid.Cell;
4283 child_fid->fid = OutFid;
4285 ret = fcache_get (&child_entry, *child_fid, ce);
4286 if (ret) {
4287 arla_warn (ADEBFCACHE, ret, "fcache_get");
4288 goto out;
4291 assert(child_entry->usage == 0);
4293 update_entry (child_entry, fetch_attr, NULL, NULL,
4294 conn, ce->cred);
4296 child_entry->flags.attrp = TRUE;
4298 ret = adir_mkdir (child_entry, child_fid->fid, dir_entry->fid.fid);
4299 if (ret) {
4300 arla_warn (ADEBFCACHE, ret, "adir_mkdir");
4301 fcache_release(child_entry);
4302 goto out;
4305 child_entry->tokens |= NNPFS_ATTR_R | NNPFS_DATA_R | NNPFS_DATA_W;
4307 fcache_release(child_entry);
4309 out:
4310 if (conn)
4311 conn_free(conn);
4312 AssertExclLocked(&dir_entry->lock);
4313 return ret;
4317 * Create a symbolic link.
4319 * Note: create_symlink->flags.kernelp is not set on success
4320 * and that must be done by the caller.
4324 create_symlink (FCacheEntry *dir_entry,
4325 const char *name, AFSStoreStatus *store_attr,
4326 VenusFid *child_fid, AFSFetchStatus *fetch_attr,
4327 const char *contents,
4328 CredCacheEntry *ce)
4330 int ret;
4331 ConnCacheEntry *conn;
4332 AFSFid OutFid;
4333 FCacheEntry *child_entry;
4334 AFSVolSync volsync;
4335 AFSFetchStatus new_status;
4336 fs_server_context context;
4338 AssertExclLocked(&dir_entry->lock);
4340 if (connected_mode != CONNECTED)
4341 return EINVAL;
4343 ret = init_fs_context(dir_entry, ce, &context);
4344 if (ret)
4345 return ret;
4347 for (conn = find_first_fs (&context);
4348 conn != NULL;
4349 conn = find_next_fs (&context, conn, ret)) {
4351 ret = RXAFS_Symlink (conn->connection,
4352 &dir_entry->fid.fid,
4353 name,
4354 contents,
4355 store_attr,
4356 &OutFid,
4357 fetch_attr,
4358 &new_status,
4359 &volsync);
4360 if (host_downp(ret)) {
4361 continue;
4362 } else if (ret) {
4363 arla_warn (ADEBFCACHE, ret, "Symlink");
4364 free_fs_server_context (&context);
4365 conn = NULL;
4366 goto out;
4368 conn_ref(conn);
4369 break;
4371 free_fs_server_context (&context);
4373 if (host_downp(ret)) {
4374 ret = ENETDOWN;
4375 goto out;
4378 update_modify_dir(dir_entry, &new_status, NULL, &volsync,
4379 conn, ce->cred);
4381 child_fid->Cell = dir_entry->fid.Cell;
4382 child_fid->fid = OutFid;
4384 ret = fcache_get (&child_entry, *child_fid, ce);
4385 if (ret) {
4386 arla_warn (ADEBFCACHE, ret, "fcache_get");
4387 goto out;
4390 update_entry (child_entry, fetch_attr, NULL, NULL,
4391 conn, ce->cred);
4394 * flags.kernelp is set in cm_symlink since the symlink
4395 * might be a mountpoint and this entry is never install
4396 * into the kernel.
4399 child_entry->flags.attrp = TRUE;
4400 child_entry->tokens |= NNPFS_ATTR_R;
4402 fcache_release(child_entry);
4404 out:
4405 if (conn)
4406 conn_free(conn);
4407 AssertExclLocked(&dir_entry->lock);
4408 return ret;
4412 * Create a hard link.
4416 create_link (FCacheEntry *dir_entry,
4417 const char *name,
4418 FCacheEntry *existing_entry,
4419 CredCacheEntry *ce)
4421 ConnCacheEntry *conn = NULL;
4422 int ret;
4423 AFSFetchStatus new_status;
4424 AFSFetchStatus status;
4425 AFSVolSync volsync;
4426 fs_server_context context;
4428 AssertExclLocked(&dir_entry->lock);
4430 if (connected_mode != CONNECTED)
4431 return EINVAL;
4433 ret = init_fs_context(dir_entry, ce, &context);
4434 if (ret)
4435 return ret;
4437 for (conn = find_first_fs (&context);
4438 conn != NULL;
4439 conn = find_next_fs (&context, conn, ret)) {
4441 ret = RXAFS_Link (conn->connection,
4442 &dir_entry->fid.fid,
4443 name,
4444 &existing_entry->fid.fid,
4445 &new_status,
4446 &status,
4447 &volsync);
4448 if (host_downp(ret)) {
4449 continue;
4450 } else if (ret) {
4451 free_fs_server_context (&context);
4452 arla_warn (ADEBFCACHE, ret, "Link");
4453 conn = NULL;
4454 goto out;
4456 conn_ref(conn);
4457 break;
4459 free_fs_server_context (&context);
4461 if (host_downp(ret)) {
4462 ret = ENETDOWN;
4463 goto out;
4466 update_modify_dir(dir_entry, &status, NULL, &volsync,
4467 conn, ce->cred);
4469 update_entry (existing_entry, &new_status, NULL, NULL,
4470 conn, ce->cred);
4472 out:
4473 if (conn)
4474 conn_free(conn);
4475 AssertExclLocked(&dir_entry->lock);
4476 return ret;
4480 * Remove a file from a directory.
4484 remove_file(FCacheEntry *dir_entry, const char *name,
4485 FCacheEntry *child_entry, CredCacheEntry *ce)
4487 int ret;
4488 ConnCacheEntry *conn;
4489 AFSFetchStatus status;
4490 AFSVolSync volsync;
4491 fs_server_context context;
4493 AssertExclLocked(&dir_entry->lock);
4494 AssertExclLocked(&child_entry->lock);
4496 if (connected_mode == CONNECTED) {
4498 ret = init_fs_context(dir_entry, ce, &context);
4499 if (ret)
4500 return ret;
4502 for (conn = find_first_fs (&context);
4503 conn != NULL;
4504 conn = find_next_fs (&context, conn, ret)) {
4506 ret = RXAFS_RemoveFile (conn->connection,
4507 &dir_entry->fid.fid,
4508 name,
4509 &status,
4510 &volsync);
4511 if (host_downp(ret)) {
4512 continue;
4513 } else if (ret) {
4514 free_fs_server_context (&context);
4515 arla_warn (ADEBFCACHE, ret, "RemoveFile");
4516 conn = NULL;
4517 goto out;
4519 conn_ref(conn);
4520 break;
4522 free_fs_server_context (&context);
4524 if (host_downp(ret))
4525 ret = ENETDOWN;
4527 } else {
4528 #if 0
4529 fbuf the_fbuf;
4530 VenusFid child_fid;
4532 status = dir_entry->status;
4534 conn = NULL;
4536 ret = fcache_get_fbuf(dir_entry, &the_fbuf, FBUF_READ);
4537 if (ret)
4538 goto out;
4540 ret = fdir_lookup(&the_fbuf, &dir_entry->fid, name, &child_fid);
4541 if (ret == 0) {
4542 FCacheEntry *child_entry = NULL;
4543 uint32_t disco_id = 0;
4545 child_entry = fcache_find(child_fid);
4546 if (child_entry)
4547 disco_id = child_entry->disco_id;
4549 disco_id = disco_unlink(&dir_entry->fid, &child_fid,
4550 name, disco_id);
4552 if (child_entry) {
4553 child_entry->disco_id = disco_id;
4554 fcache_release(child_entry);
4558 abuf_end (&the_fbuf);
4559 #else
4560 ret = EINVAL; /* XXX */
4561 #endif
4564 if (ret == 0) {
4565 update_modify_dir(dir_entry, &status, NULL, &volsync,
4566 conn, ce->cred);
4567 child_entry->status.LinkCount--;
4568 if (child_entry->status.LinkCount == 0)
4569 child_entry->flags.silly = TRUE;
4572 out:
4573 if (conn)
4574 conn_free(conn);
4575 AssertExclLocked(&dir_entry->lock);
4576 return ret;
4580 * Remove a directory from a directory.
4584 remove_directory(FCacheEntry *dir_entry,
4585 const char *name,
4586 FCacheEntry *child_entry,
4587 CredCacheEntry *ce)
4589 int ret;
4590 ConnCacheEntry *conn;
4591 AFSFetchStatus status;
4592 AFSVolSync volsync;
4593 fs_server_context context;
4595 AssertExclLocked(&dir_entry->lock);
4597 if (connected_mode != CONNECTED)
4598 return EINVAL;
4600 ret = init_fs_context(dir_entry, ce, &context);
4601 if (ret)
4602 return ret;
4604 for (conn = find_first_fs (&context);
4605 conn != NULL;
4606 conn = find_next_fs (&context, conn, ret)) {
4608 ret = RXAFS_RemoveDir (conn->connection,
4609 &dir_entry->fid.fid,
4610 name,
4611 &status,
4612 &volsync);
4613 if (host_downp(ret)) {
4614 continue;
4615 } else if (ret) {
4616 free_fs_server_context (&context);
4617 arla_warn (ADEBFCACHE, ret, "RemoveDir");
4618 conn = NULL;
4619 goto out;
4621 conn_ref(conn);
4622 break;
4624 free_fs_server_context (&context);
4626 if (host_downp(ret)) {
4627 ret = ENETDOWN;
4628 goto out;
4631 update_modify_dir(dir_entry, &status, NULL, &volsync,
4632 conn, ce->cred);
4634 if (child_entry->status.LinkCount == 2) {
4635 child_entry->status.LinkCount = 0;
4636 child_entry->flags.silly = TRUE;
4639 out:
4640 if (conn)
4641 conn_free(conn);
4642 AssertExclLocked(&dir_entry->lock);
4643 return ret;
4647 * Rename a file
4651 rename_file (FCacheEntry *old_dir,
4652 const char *old_name,
4653 FCacheEntry *new_dir,
4654 const char *new_name,
4655 CredCacheEntry *ce)
4657 int ret = ARLA_CALL_DEAD;
4658 ConnCacheEntry *conn;
4659 AFSFetchStatus orig_status, new_status;
4660 AFSVolSync volsync;
4661 fs_server_context context;
4663 AssertExclLocked(&old_dir->lock);
4664 AssertExclLocked(&new_dir->lock);
4666 if (connected_mode != CONNECTED)
4667 return EINVAL;
4669 ret = init_fs_context(old_dir, ce, &context);
4670 if (ret)
4671 return ret;
4673 for (conn = find_first_fs (&context);
4674 conn != NULL;
4675 conn = find_next_fs (&context, conn, ret)) {
4677 ret = RXAFS_Rename (conn->connection,
4678 &old_dir->fid.fid,
4679 old_name,
4680 &new_dir->fid.fid,
4681 new_name,
4682 &orig_status,
4683 &new_status,
4684 &volsync);
4685 if (host_downp(ret)) {
4686 continue;
4687 } else if (ret) {
4688 free_fs_server_context (&context);
4689 arla_warn (ADEBFCACHE, ret, "Rename");
4690 conn = NULL;
4691 goto out;
4693 conn_ref(conn);
4694 break;
4696 free_fs_server_context (&context);
4698 if (host_downp(ret)) {
4699 ret = ENETDOWN;
4700 goto out;
4704 if (old_dir != new_dir)
4705 update_modify_dir(old_dir, &orig_status, NULL, &volsync,
4706 conn, ce->cred);
4708 update_modify_dir(new_dir, &new_status, NULL, &volsync,
4709 conn, ce->cred);
4711 out:
4712 if (conn)
4713 conn_free(conn);
4714 AssertExclLocked(&old_dir->lock);
4715 AssertExclLocked(&new_dir->lock);
4716 return ret;
4720 * Return the fid to the root.
4724 getroot (VenusFid *res, CredCacheEntry *ce)
4726 VolCacheEntry *ve;
4727 VenusFid fid;
4728 const char *root_volume = volcache_get_rootvolume ();
4729 int ret;
4730 const char *this_cell = cell_getthiscell ();
4731 int32_t this_cell_id;
4733 if (dynroot_enablep()) {
4734 this_cell = "dynroot";
4735 this_cell_id = dynroot_cellid();
4736 } else {
4737 this_cell_id = cell_name2num (this_cell);
4738 if (this_cell_id == -1)
4739 arla_errx (1, ADEBERROR, "cell %s does not exist", this_cell);
4742 ret = volcache_getbyname (root_volume, this_cell_id, ce, &ve, NULL);
4743 if (ret) {
4744 arla_warn (ADEBWARN, ret,
4745 "Cannot find the root volume (%s) in cell %s",
4746 root_volume, this_cell);
4747 return ret;
4750 fid.Cell = this_cell_id;
4751 if (ve->entry.flags & VLF_ROEXISTS) {
4752 fid.fid.Volume = ve->entry.volumeId[ROVOL];
4753 } else if (ve->entry.flags & VLF_RWEXISTS) {
4754 arla_warnx(ADEBERROR,
4755 "getroot: %s in cell %s is missing a RO clone, not good",
4756 root_volume, this_cell);
4757 fid.fid.Volume = ve->entry.volumeId[RWVOL];
4758 } else {
4759 arla_errx(1, ADEBERROR,
4760 "getroot: %s in cell %s has no RW or RO clone?",
4761 root_volume, this_cell);
4763 fid.fid.Vnode = fid.fid.Unique = 1;
4765 volcache_free (ve);
4767 *res = fid;
4768 return 0;
4772 * Return the type for this volume.
4775 long
4776 getvoltype(int32_t volid, const VolCacheEntry *ve)
4778 int i;
4780 for (i = RWVOL; i <= BACKVOL; ++i)
4781 if (ve->entry.volumeId[i] == volid)
4782 return i;
4783 assert (FALSE);
4784 return -1; /* NOT REACHED */
4788 * Return locked entry for `fid' or NULL. If `gcp' is set the
4789 * returned node may be under gc.
4792 FCacheEntry *
4793 fcache_find_gcp(VenusFid fid, Bool gcp)
4795 FCacheEntry *res = find_entry_nolock(fid);
4796 if (res == NULL)
4797 return res;
4799 res->refcount++;
4800 assert(res->refcount > 0);
4802 fcache_lock(res, gcp);
4803 if (!gcp)
4804 assert(!res->flags.gcp);
4806 return res;
4810 * Return locked entry for `fid' or NULL.
4813 FCacheEntry *
4814 fcache_find(VenusFid fid)
4816 return fcache_find_gcp(fid, FALSE);
4820 * Return the entry for `fid'. If it's not cached, add it.
4823 static int
4824 fcache_get_int(FCacheEntry **res, VenusFid fid, CredCacheEntry *ce, Bool gcp)
4826 FCacheEntry *old;
4827 FCacheEntry *e;
4828 VolCacheEntry *vol;
4829 int i, error;
4831 *res = NULL;
4833 old = fcache_find_gcp(fid, gcp);
4834 if (old) {
4835 assert (old->flags.usedp);
4836 *res = old;
4837 return 0;
4840 error = volcache_getbyid (fid.fid.Volume, fid.Cell, ce, &vol, NULL);
4841 if (error) {
4842 if (connected_mode == DISCONNECTED && error == ENOENT)
4843 return ENETDOWN;
4844 return error;
4847 e = find_free_entry ();
4848 assert (e != NULL);
4850 old = fcache_find_gcp(fid, gcp);
4851 if (old) {
4852 AssertExclLocked(&e->lock);
4853 fcache_unlock(e);
4855 e->lru_le = listaddtail(free_nodes, e);
4856 assert(e->lru_le);
4858 assert (old->flags.usedp);
4859 *res = old;
4860 return 0;
4864 assert(e->blocks);
4865 assert(listemptyp(e->blocks));
4867 e->fid = fid;
4868 e->refcount = 1;
4869 e->host = 0;
4870 e->usage = 0;
4871 memset (&e->status, 0, sizeof(e->status));
4872 memset (&e->callback, 0, sizeof(e->callback));
4873 memset (&e->volsync, 0, sizeof(e->volsync));
4874 for (i = 0; i < NACCESS; i++) {
4875 e->acccache[i].cred = ARLA_NO_AUTH_CRED;
4876 e->acccache[i].access = 0;
4878 e->anonaccess = 0;
4879 e->flags.usedp = TRUE;
4880 e->flags.attrp = FALSE;
4881 e->flags.attrusedp = FALSE;
4882 e->flags.datausedp = FALSE;
4883 e->flags.extradirp = FALSE;
4884 e->flags.mountp = FALSE;
4885 e->flags.fake_mp = FALSE;
4886 e->flags.vol_root = FALSE;
4887 e->flags.kernelp = FALSE;
4888 e->flags.sentenced = FALSE;
4889 e->flags.stale = FALSE;
4890 e->flags.dirtied = FALSE;
4891 e->flags.silly = FALSE;
4892 e->flags.waiters = FALSE;
4893 e->flags.gcp = FALSE;
4894 e->flags.appended = FALSE;
4895 e->tokens = 0;
4896 memset (&e->parent, 0, sizeof(e->parent));
4897 e->lru_le = listaddhead (node_lru, e);
4898 assert(e->lru_le);
4899 e->invalid_ptr = -1;
4900 e->volume = vol;
4901 e->priority = fprio_get(fid);
4902 e->hits = 0;
4904 hashtabadd (hashtab, e);
4906 *res = e;
4907 return 0;
4911 * Return the entry for `fid'. If it's not cached, add it.
4915 fcache_get(FCacheEntry **res, VenusFid fid, CredCacheEntry *ce)
4917 return fcache_get_int(res, fid, ce, FALSE);
4921 * Return the entry for `fid'. If it's not cached, add it.
4925 fcache_get_gc(FCacheEntry **res, VenusFid fid, CredCacheEntry *ce)
4927 return fcache_get_int(res, fid, ce, TRUE);
4931 * Release the lock on `e' and mark it as stale if it has been sentenced.
4934 void
4935 fcache_release(FCacheEntry *e)
4937 AssertExclLocked(&e->lock);
4939 e->refcount--;
4941 assert(e->refcount >= 0);
4943 fcache_unlock(e);
4945 if (e->flags.sentenced) {
4946 e->flags.sentenced = FALSE;
4947 stale(e, broken_callback);
4955 static Bool
4956 uptodatep (FCacheEntry *e)
4958 struct timeval tv;
4959 assert (e->flags.usedp);
4961 if (connected_mode != CONNECTED &&
4962 connected_mode != FETCH_ONLY)
4963 return TRUE;
4965 gettimeofday(&tv, NULL);
4967 if (tv.tv_sec < e->callback.ExpirationTime &&
4968 e->callback.CallBackType != CBDROPPED &&
4969 (e->callback.CallBackType != 0
4970 || e->volume->volsync.spare1 != e->volsync.spare1))
4971 return TRUE;
4973 return FALSE;
4977 * The idea is that we start to stat everything after the prefered
4978 * entry, everything before that is probably not useful to get, the
4979 * user is probably trying to stat() everything _after_ that node.
4980 * This might be somewhat bogus, but we dont care (for now).
4983 struct bulkstat {
4984 int len; /* used entries in fids and names */
4985 AFSFid fids[AFSCBMAX]; /* fids to fetch */
4986 char *names[AFSCBMAX]; /* names it install */
4987 AFSFid *used; /* do we have a prefered node */
4988 CredCacheEntry *ce; /* cred to use */
4991 typedef union {
4992 struct nnpfs_message_installnode node;
4993 struct nnpfs_message_installattr attr;
4994 } nnpfs_message_install_node_attr;
4996 static int
4997 bulkstat_help_func (VenusFid *fid, const char *name, void *ptr)
4999 struct bulkstat *bs = (struct bulkstat *) ptr;
5000 AccessEntry *ae;
5001 FCacheEntry key;
5002 FCacheEntry *e;
5004 /* Is bs full ? */
5005 if (bs->len > fcache_bulkstatus_num)
5006 return 0;
5008 /* Ignore . and .. */
5009 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
5010 return 0;
5013 * Do we have a prefered node, and is this the one. If we don't know
5014 * the name of the node (ie bs.names[0] == NULL), fill it in.
5015 * Set bs->used to NULL it indicate that we should start stat stuff
5016 * from here, remeber that bs->len == 1 if bs->used is set.
5018 if (bs->used) {
5019 if (memcmp(bs->used, &fid->fid, sizeof(fid->fid)) == 0) {
5020 if (bs->names[0] == NULL)
5021 bs->names[0] = strdup (name);
5022 bs->used = NULL; /* stat everything after this */
5024 return 0;
5028 * Already cached for this pag ?
5030 key.fid = *fid;
5031 e = (FCacheEntry *)hashtabsearch (hashtab, (void *)&key);
5032 if (e
5033 && e->flags.usedp
5034 && e->flags.attrp
5035 && uptodatep (e)
5036 && findaccess (bs->ce->cred, e->acccache, &ae) == TRUE) {
5037 arla_warnx (ADEBFCACHE,
5038 "bulkstat_help_func: already cached "
5039 "(%d.%d.%d.%d) name: %s",
5040 fid->Cell, fid->fid.Volume, fid->fid.Vnode,
5041 fid->fid.Unique, name);
5042 return 0;
5045 if (fcache_enable_bulkstatus == 2) {
5046 /* cache the name for the installnode */
5047 bs->names[bs->len] = strdup (name);
5048 if (bs->names[bs->len] == NULL)
5049 return 0;
5050 } else {
5051 bs->names[bs->len] = NULL;
5055 bs->fids[bs->len] = fid->fid;
5056 bs->len++;
5058 return 0;
5062 * Do bulkstat for ``parent_entry''. Make sure that ``prefered_entry''
5063 * is in the list of fids it not NULL, and it ``prefered_name'' is NULL
5064 * try to find it in the list files in the directory.
5066 * Entry Success Failure
5067 * parent_entry locked locked locked
5068 * prefered_entry locked locked locked
5069 * or if NULL if set to NULL must not be locked
5070 * prefered_fid related fcache-entry must not be locked
5071 * ce not NULL
5074 static int
5075 get_attr_bulk (FCacheEntry *parent_entry,
5076 FCacheEntry *prefered_entry,
5077 VenusFid *prefered_fid,
5078 const char *prefered_name,
5079 CredCacheEntry *ce)
5081 fs_server_context context;
5082 ConnCacheEntry *conn = NULL;
5083 struct bulkstat bs;
5084 AFSBulkStats stats;
5085 AFSVolSync sync;
5086 AFSCBFids fids;
5087 fbuf the_fbuf;
5088 int ret;
5089 AFSCBs cbs;
5090 int i;
5091 int len;
5092 struct collect_stat collectstat;
5094 arla_warnx (ADEBFCACHE, "get_attr_bulk");
5096 AssertExclLocked(&parent_entry->lock);
5097 if (prefered_entry)
5098 AssertExclLocked(&prefered_entry->lock);
5100 if (fcache_enable_bulkstatus == 0)
5101 return -1;
5103 if (parent_entry->usage == 0) {
5104 arla_warnx (ADEBFCACHE, "get_attr_bulk: parent doesn't have data");
5105 return -1;
5108 fids.val = bs.fids;
5110 memset (bs.names, 0, sizeof(bs.names));
5111 memset (bs.fids, 0, sizeof(bs.fids));
5112 bs.len = 0;
5113 bs.ce = ce;
5114 bs.used = NULL;
5117 * If we have a prefered_entry, and that to the first entry in the
5118 * array. This is used later. If we find the prefered_entry in the
5119 * directory-structure its ignored.
5122 if (prefered_fid) {
5123 arla_warnx (ADEBFCACHE, "get_attr_bulk: using prefered_entry");
5124 bs.used = &prefered_fid->fid;
5125 fids.val[bs.len] = prefered_fid->fid;
5126 if (prefered_name != NULL) {
5127 bs.names[bs.len] = strdup(prefered_name);
5128 if (bs.names[bs.len] == NULL)
5129 return ENOMEM;
5130 } else {
5131 bs.names[bs.len] = NULL;
5133 bs.len++;
5136 ret = fcache_get_fbuf (parent_entry, &the_fbuf, FBUF_READ);
5137 if (ret)
5138 return ret;
5140 ret = fdir_readdir (&the_fbuf,
5141 bulkstat_help_func,
5142 &bs,
5143 parent_entry->fid,
5144 NULL);
5145 abuf_end (&the_fbuf);
5146 if (ret)
5147 goto out_names;
5149 fids.len = bs.len;
5152 * Don't do BulkStatus when fids.len == 0 since we should never do it.
5153 * There should at least be the node that we want in the BulkStatus.
5156 if (fids.len == 0) {
5157 if (prefered_fid)
5158 arla_warnx (ADEBERROR,
5159 "get_attr_bulk: "
5160 "prefered_fid not found in dir");
5161 /* XXX MAGIC send it back so we don't do it again soon */
5162 parent_entry->hits -= 64;
5163 ret = EINVAL;
5164 goto out_names;
5168 * XXX if there is a prefered fid, and and we didn't find the name for it
5169 * return an error.
5172 if (prefered_fid && bs.names[0] == NULL) {
5173 arla_warnx (ADEBFCACHE,
5174 "get_attr_bulk: didn't find prefered_fid's name");
5175 ret = EINVAL;
5176 goto out_names;
5179 ret = ARLA_CALL_DEAD;
5181 ret = init_fs_context(parent_entry, ce, &context);
5182 if (ret)
5183 return ret;
5185 for (conn = find_first_fs (&context);
5186 conn != NULL;
5187 conn = find_next_fs (&context, conn, ret)) {
5189 stats.val = NULL;
5190 cbs.val = NULL;
5191 stats.len = cbs.len = 0;
5193 collectstats_start(&collectstat);
5194 ret = RXAFS_BulkStatus (conn->connection, &fids, &stats, &cbs, &sync);
5195 collectstats_stop(&collectstat, parent_entry, conn,
5196 find_partition(&context),
5197 arla_STATISTICS_REQTYPE_BULKSTATUS, fids.len);
5198 if (ret) {
5199 free (stats.val);
5200 free (cbs.val);
5203 if (host_downp(ret)) {
5204 continue;
5205 } else if (ret) {
5206 free_fs_server_context(&context);
5207 arla_warn(ADEBFCACHE, ret, "BulkStatus");
5208 conn = NULL;
5209 goto out_names;
5211 conn_ref(conn);
5212 break;
5215 free_fs_server_context (&context);
5217 if (ret) {
5218 ret = ENETDOWN;
5219 goto out_names;
5222 arla_warnx (ADEBFCACHE,"get_attr_bulk: BulkStatus returned %d",ret);
5224 len = min(fids.len, min(stats.len, cbs.len));
5227 * Save results of bulkstatus
5230 if (ret == 0) {
5231 FCacheEntry *e;
5232 VenusFid fid;
5234 fcache_counter.fetch_attr_bulk += len;
5236 fid.Cell = parent_entry->fid.Cell;
5237 for (i = 0; i < len && ret == 0; i++) {
5239 fid.fid = fids.val[i];
5241 if (VenusFid_cmp(prefered_fid, &fid) == 0) {
5242 e = prefered_entry;
5243 } else {
5244 e = find_entry_nolock (fid);
5245 if (e != NULL && fcache_islocked(e))
5246 continue;
5248 ret = fcache_get (&e, fid, ce);
5249 if (ret)
5250 break;
5252 update_attr_entry (e,
5253 &stats.val[i],
5254 &cbs.val[i],
5255 &sync,
5256 conn,
5257 ce->cred);
5258 e->parent = parent_entry->fid;
5259 if (prefered_entry != e) {
5260 fcache_release(e);
5266 * Insert result into kernel
5269 if (fcache_enable_bulkstatus == 2 && ret == 0) {
5270 nnpfs_message_install_node_attr msg[AFSCBMAX];
5271 struct nnpfs_msg_node *node;
5272 nnpfs_handle *parent;
5273 FCacheEntry *e;
5274 VenusFid fid;
5275 int j;
5277 fid.Cell = parent_entry->fid.Cell;
5278 for (i = 0 , j = 0; i < len && ret == 0; i++) {
5279 u_int tokens;
5281 fid.fid = fids.val[i];
5283 if (VenusFid_cmp(prefered_fid, &fid) == 0) {
5284 e = prefered_entry;
5285 } else {
5286 e = find_entry_nolock (fid);
5287 if (e != NULL && fcache_islocked(e))
5288 continue;
5290 ret = fcache_get (&e, fid, ce);
5291 if (ret)
5292 break;
5296 arla_warnx (ADEBFCACHE, "installing %d.%d.%d\n",
5297 e->fid.fid.Volume,
5298 e->fid.fid.Vnode,
5299 e->fid.fid.Unique);
5300 assert_flag(e,kernelp);
5301 e->flags.attrusedp = TRUE;
5304 * Its its already installed, just update with installattr
5307 e->tokens |= NNPFS_ATTR_R;
5308 tokens = e->tokens;
5309 if (!e->flags.kernelp || !e->flags.datausedp)
5310 tokens &= ~NNPFS_DATA_MASK;
5312 if (e->flags.kernelp) {
5313 msg[j].attr.header.opcode = NNPFS_MSG_INSTALLATTR;
5314 node = &msg[j].attr.node;
5315 parent = NULL;
5316 } else {
5317 msg[j].node.header.opcode = NNPFS_MSG_INSTALLNODE;
5318 node = &msg[j].node.node;
5319 parent = &msg[j].node.parent_handle;
5320 e->flags.kernelp = TRUE;
5321 strlcpy (msg[j].node.name, bs.names[i],
5322 sizeof(msg[j].node.name));
5324 node->tokens = tokens;
5327 * Don't install symlink since they might be
5328 * mount-points.
5331 if (e->status.FileType != TYPE_LINK) {
5332 fcacheentry2nnpfsnode (&e->fid,
5333 &e->fid,
5334 &stats.val[i],
5335 node,
5336 parent_entry->acccache,
5337 FCACHE2NNPFSNODE_ALL);
5339 if (parent)
5340 *parent = *(struct nnpfs_handle*) &parent_entry->fid;
5341 msg[j].attr.flag = 0;
5342 j++;
5344 if (prefered_entry != e)
5345 fcache_release(e);
5349 * Install if there is no error and we have something to install
5352 if (ret == 0 && j != 0)
5353 ret = nnpfs_send_message_multiple_list (kernel_fd,
5354 (struct nnpfs_message_header *) msg,
5355 sizeof (msg[0]),
5357 /* We have what we wanted, ignore errors */
5358 if (ret && i > 0 && prefered_entry)
5359 ret = 0;
5362 free (stats.val);
5363 free (cbs.val);
5365 out_names:
5366 for (i = 0 ; i < bs.len && ret == 0; i++)
5367 free (bs.names[i]);
5369 if (conn)
5370 conn_free(conn);
5372 arla_warnx (ADEBFCACHE, "get_attr_bulk: returned %d", ret);
5374 return ret;
5379 * fetch attributes for the note `entry' with the rights `ce'. If
5380 * `parent_entry' is not NULL, it is used for doing bulkstatus when
5381 * guess is necessary. If there is a named associated with `entry' it
5382 * should be filled into `prefered_name' as that will be used for
5383 * guessing that nodes should be bulkstat:ed.
5385 * If there is no bulkstatus done, a plain FetchStatus is done.
5389 fcache_verify_attr (FCacheEntry *entry, FCacheEntry *parent,
5390 const char *prefered_name, CredCacheEntry* ce)
5392 AccessEntry *ae;
5394 if (dynroot_is_dynrootp (entry))
5395 return dynroot_get_attr (entry, ce);
5397 if (entry->flags.usedp
5398 && entry->flags.attrp
5399 && uptodatep(entry)
5400 && findaccess (ce->cred, entry->acccache, &ae) == TRUE)
5402 arla_warnx (ADEBFCACHE, "fcache_get_attr: have attr");
5403 fcache_counter.fetch_attr_cached++;
5404 return 0;
5408 * XXX is this right ?
5409 * Dont ask fileserver if this file is deleted
5411 if (entry->flags.silly) {
5412 entry->tokens |= NNPFS_ATTR_R;
5413 entry->flags.attrp = TRUE;
5414 return 0;
5417 if (connected_mode == DISCONNECTED) {
5418 if (entry->flags.attrp) {
5419 AccessEntry *ae;
5420 findaccess(ce->cred, entry->acccache, &ae);
5421 ae->cred = ce->cred;
5422 ae->access = 0x7f; /* XXXDISCO */
5423 return 0;
5425 else
5426 return ENETDOWN;
5430 * If there is no parent, `entry' is a root-node, or the parent is
5431 * un-initialized, don't bother bulkstatus.
5433 if (parent != NULL
5434 && entry->fid.fid.Vnode != 1
5435 && entry->fid.fid.Unique != 1
5436 && !entry->flags.mountp
5437 && !entry->flags.fake_mp
5438 && entry->parent.Cell != 0
5439 && entry->parent.fid.Volume != 0
5440 && entry->parent.fid.Vnode != 0
5441 && entry->parent.fid.Unique != 0)
5444 * Check if the entry is used, that means that
5445 * there is greater chance that we we'll succeed
5446 * when doing bulkstatus.
5449 if (parent->hits++ > fcache_bulkstatus_num &&
5450 parent->flags.datausedp) {
5451 int error;
5453 arla_warnx (ADEBFCACHE, "fcache_get_attr: doing bulk get_attr");
5455 error = get_attr_bulk (parent,
5456 entry, &entry->fid,
5457 prefered_name, ce);
5458 /* magic calculation when we are going to do next bulkstat */
5459 parent->hits = 0;
5461 if (error == 0)
5462 return 0;
5467 * We got here because the bulkstatus failed, didn't want to do a
5468 * bulkstatus or we didn't get a parent for the entry
5471 arla_warnx (ADEBFCACHE, "fcache_get_attr: doing read_attr");
5473 return read_attr (entry, ce);
5479 * Make sure that `e' has attributes and that they are up-to-date.
5480 * `e' must be write-locked.
5483 static int
5484 do_read_data(FCacheEntry *e, CredCacheEntry *ce,
5485 uint64_t offset, uint64_t end)
5487 int ret = ARLA_CALL_DEAD;
5488 fs_server_context context;
5489 ConnCacheEntry *conn;
5491 if (connected_mode == DISCONNECTED)
5492 return ENETDOWN;
5494 ret = init_fs_context(e, ce, &context);
5495 if (ret)
5496 return ret;
5498 for (conn = find_first_fs (&context);
5499 conn != NULL;
5500 conn = find_next_fs (&context, conn, ret)) {
5501 do {
5502 ret = read_data(e, conn, ce, find_partition(&context),
5503 offset, end);
5504 } while (!ret && !fcache_have_wanted(e, offset, end));
5505 if (!try_next_fs (ret, &e->fid))
5506 break;
5508 free_fs_server_context (&context);
5510 if (host_downp(ret))
5511 ret = ENETDOWN;
5512 return ret;
5516 * Make sure that `e' has file data and is up-to-date.
5519 static int
5520 fcache_verify_data(FCacheEntry *e, CredCacheEntry *ce,
5521 uint64_t offset, uint64_t end)
5523 ConnCacheEntry *conn = NULL;
5524 int ret;
5525 fs_server_context context;
5527 assert (e->flags.usedp);
5528 AssertExclLocked(&e->lock);
5530 if (dynroot_is_dynrootp (e))
5531 return dynroot_get_data (e, ce);
5533 /* Don't get data for deleted files */
5534 if (e->flags.silly) {
5535 if (fcache_have_wanted(e, offset, end))
5536 return 0;
5538 return EIO;
5541 if (e->flags.attrp && uptodatep(e)) {
5543 /* For directories we have all data or no data at all */
5544 if (e->status.FileType == TYPE_DIR
5545 && block_any(e) != BLOCK_NONE) /* XXX */
5546 return 0;
5548 if (fcache_have_wanted(e, offset, end)) {
5549 fcache_counter.fetch_data_cached++;
5550 return 0;
5551 } else
5552 return do_read_data(e, ce, offset, end);
5555 ret = do_read_attr (e, ce, &conn, &context);
5556 if (ret)
5557 return ret;
5559 if (fcache_have_wanted(e, offset, end)) {
5560 fcache_counter.fetch_data_cached++;
5561 free_fs_server_context (&context);
5562 return 0;
5565 do {
5566 ret = read_data(e, conn, ce, find_partition(&context),
5567 offset, end);
5568 } while (!ret && !fcache_have_wanted(e, offset, end));
5570 free_fs_server_context (&context);
5571 return ret;
5575 * Fetch `fid' with data, returning the cache entry in `res'.
5576 * note that `fid' might change.
5580 fcache_get_data(FCacheEntry **e, CredCacheEntry **ce,
5581 uint64_t wanted_offset, uint64_t wanted_end)
5583 int ret;
5585 if ((*e)->flags.fake_mp) {
5586 VenusFid new_fid;
5587 FCacheEntry *new_root;
5589 ret = resolve_mp(*e, &new_fid, ce);
5590 if (ret) {
5591 return ret;
5593 ret = fcache_get (&new_root, new_fid, *ce);
5594 if (ret) {
5595 return ret;
5597 ret = fcache_verify_attr (new_root, NULL, NULL, *ce);
5598 if (ret) {
5599 fcache_release (new_root);
5600 return ret;
5602 (*e)->flags.fake_mp = FALSE;
5603 (*e)->flags.mountp = TRUE;
5604 (*e)->status.FileType = TYPE_LINK;
5605 update_fid ((*e)->fid, *e, new_fid, new_root);
5606 fcache_release (*e);
5607 *e = new_root;
5608 install_attr (*e, FCACHE2NNPFSNODE_ALL);
5611 if (wanted_end == 0) {
5613 * XXX remove this case, attr should either be known already
5614 * here, or we should just fetch `whole file'/next block.
5617 ret = fcache_verify_attr (*e, NULL, NULL, *ce);
5618 if (ret)
5619 return ret;
5621 /* if ((*e)->usage == 0 || !uptodatep(*e)) */
5622 wanted_end = fcache_get_status_length(&(*e)->status);
5625 ret = fcache_verify_data(*e, *ce, wanted_offset, wanted_end);
5626 return ret;
5630 * Helper function for followmountpoint.
5631 * Given the contents of a mount-point, figure out the cell and volume name.
5633 * ``mp'' must be writeable and should not be used afterwards.
5634 * ``*volname'' is a pointer to somewhere in the mp string.
5635 * ``cell'' should be set before function is called to default cell.
5638 static int
5639 parse_mountpoint (char *mp, size_t len, int32_t *cell, char **volname)
5641 char *colon;
5643 mp[len - 1] = '\0';
5644 colon = strchr (mp, ':');
5645 if (colon != NULL) {
5646 *colon++ = '\0';
5647 *cell = cell_name2num (mp + 1);
5648 if (*cell == -1)
5649 return ENOENT;
5650 *volname = colon;
5651 } else {
5652 *volname = mp + 1;
5654 return 0;
5658 * Used by followmountpoint to figure out what clone of a volume
5659 * should be used.
5661 * Given a `volname', `cell', it uses the given `ce', `mount_symbol'
5662 * and `parent_type' to return a volume id in `volume'.
5664 * The rules are:
5666 * "readonly" -> RO
5667 * BK + "backup" -> fail
5668 * "backup" -> BK
5669 * BK + "" + # -> RO
5670 * RO + "" + # -> RO
5671 * * -> RW
5673 * this_type = "" | "readonly" | "backup"
5674 * parent_type = RW | RO | BK
5675 * mount_symbol = "#" | "%"
5678 static int
5679 find_volume (const char *volname, int32_t cell,
5680 CredCacheEntry *ce, char mount_symbol, int parent_type,
5681 uint32_t *volid, VolCacheEntry **ve)
5683 int result_type;
5684 int this_type;
5685 int res;
5687 res = volcache_getbyname (volname, cell, ce, ve, &this_type);
5688 if (res)
5689 return res;
5691 assert (this_type == RWVOL ||
5692 this_type == ROVOL ||
5693 this_type == BACKVOL);
5695 if (this_type == ROVOL) {
5696 if (!((*ve)->entry.flags & VLF_ROEXISTS)) {
5697 volcache_free (*ve);
5698 return ENOENT;
5700 result_type = ROVOL;
5701 } else if (this_type == BACKVOL && parent_type == BACKVOL) {
5702 volcache_free (*ve);
5703 return ENOENT;
5704 } else if (this_type == BACKVOL) {
5705 if (!((*ve)->entry.flags & VLF_BOEXISTS)) {
5706 volcache_free (*ve);
5707 return ENOENT;
5709 result_type = BACKVOL;
5710 } else if (this_type == RWVOL &&
5711 parent_type != RWVOL &&
5712 mount_symbol == '#') {
5713 if ((*ve)->entry.flags & VLF_ROEXISTS)
5714 result_type = ROVOL;
5715 else if ((*ve)->entry.flags & VLF_RWEXISTS)
5716 result_type = RWVOL;
5717 else {
5718 volcache_free (*ve);
5719 return ENOENT;
5721 } else {
5722 if ((*ve)->entry.flags & VLF_RWEXISTS)
5723 result_type = RWVOL;
5724 else if ((*ve)->entry.flags & VLF_ROEXISTS)
5725 result_type = ROVOL;
5726 else {
5727 volcache_free (*ve);
5728 return ENOENT;
5731 *volid = (*ve)->entry.volumeId[result_type];
5732 return 0;
5736 * Set `fid' to point to the root of the volume pointed to by the
5737 * mount-point in (buf, len).
5739 * If succesful, `fid' will be updated to the root of the volume, and
5740 * `ce' will point to a cred in the new cell.
5743 static int
5744 get_root_of_volume (VenusFid *fid, const VenusFid *parent,
5745 VolCacheEntry *volume,
5746 CredCacheEntry **ce,
5747 char *buf, size_t len)
5749 VenusFid oldfid = *fid;
5750 char *volname;
5751 int32_t cell;
5752 uint32_t volid;
5753 int res;
5754 long parent_type, voltype;
5755 char mount_symbol;
5756 VolCacheEntry *ve;
5757 FCacheEntry *e;
5759 cell = fid->Cell;
5761 res = parse_mountpoint (buf, len, &cell, &volname);
5762 if (res)
5763 return res;
5766 * If this is a cross-cell mountpoint we need new credentials.
5769 if ((*ce)->cell != cell) {
5770 CredCacheEntry *new_ce;
5772 new_ce = cred_get(cell, (*ce)->cred, CRED_ANY);
5773 if (new_ce == NULL)
5774 return ENOMEM;
5775 cred_free (*ce);
5776 *ce = new_ce;
5779 parent_type = getvoltype (fid->fid.Volume, volume);
5780 mount_symbol = *buf;
5782 res = find_volume (volname, cell, *ce, mount_symbol,
5783 parent_type, &volid, &ve);
5784 if (res)
5785 return res;
5788 * Create the new fid. The root of a volume always has
5789 * (Vnode, Unique) = (1,1)
5792 fid->Cell = cell;
5793 fid->fid.Volume = volid;
5794 fid->fid.Vnode = fid->fid.Unique = 1;
5797 * Check if we are looking up ourself, if we are, just return.
5800 if (VenusFid_cmp(fid, parent) == 0) {
5801 volcache_free (ve);
5802 return 0;
5805 res = fcache_get (&e, *fid, *ce);
5806 if (res) {
5807 volcache_free (ve);
5808 return res;
5812 * Root nodes are a little bit special. We keep track of
5813 * their parent in `parent' so that `..' can be handled
5814 * properly.
5817 e->flags.vol_root = TRUE;
5818 e->parent = *parent;
5820 voltype = getvoltype (fid->fid.Volume, ve);
5821 if (ve->parent[voltype].volume == NULL) {
5822 ve->parent[voltype].fid = *parent;
5823 ve->parent[voltype].mp_fid = oldfid;
5825 volcache_volref (ve, volume, voltype);
5826 fcache_release (e);
5827 volcache_free (ve);
5828 return 0;
5832 * If this entry is a mount point, set the fid data to
5833 * the root directory of the volume it's pointing at,
5834 * otherwise just leave it.
5836 * Mount points are symbol links with the following contents:
5838 * '#' | '%' [ cell ':' ] volume-name [ '.' ]
5840 * This function tries to do a minimal amount of work. It always has
5841 * to fetch the attributes of `fid' and if it's a symbolic link, the
5842 * contents as well.
5846 followmountpoint (VenusFid *fid, const VenusFid *parent, FCacheEntry *parent_e,
5847 CredCacheEntry **ce)
5849 FCacheEntry *e;
5850 int ret;
5853 * Get the node for `fid' and verify that it's a symbolic link
5854 * with the correct bits. Otherwise, just return the old
5855 * `fid' without any change.
5858 ret = fcache_get (&e, *fid, *ce);
5859 if (ret)
5860 return ret;
5862 e->parent = *parent;
5863 ret = fcache_verify_attr (e, parent_e, NULL, *ce);
5864 if (ret) {
5865 fcache_release(e);
5866 return ret;
5869 if (e->flags.mountp)
5870 ret = resolve_mp(e, fid, ce);
5872 fcache_release(e);
5873 return ret;
5877 * actually resolve a mount-point
5880 static int
5881 resolve_mp(FCacheEntry *e, VenusFid *ret_fid, CredCacheEntry **ce)
5883 VenusFid fid = e->fid;
5884 int ret;
5885 fbuf the_fbuf;
5886 char *buf;
5887 uint32_t length;
5889 assert(e->flags.fake_mp || e->flags.mountp);
5890 AssertExclLocked(&e->lock);
5892 ret = fcache_verify_data(e, *ce, 0,
5893 fcache_get_status_length(&e->status));
5894 if (ret)
5895 return ret;
5897 length = fcache_get_status_length(&e->status);
5899 ret = abuf_create (&the_fbuf, e, length, FBUF_READ);
5900 if (ret)
5901 return ret;
5903 buf = fbuf_buf (&the_fbuf);
5905 ret = get_root_of_volume (&fid, &e->parent, e->volume,
5906 ce, buf, length);
5908 abuf_end (&the_fbuf);
5909 if (ret)
5910 return ret;
5911 *ret_fid = fid;
5912 return 0;
5919 static Bool
5920 print_entry (void *ptr, void *arg)
5922 FCacheEntry *e = (FCacheEntry *)ptr;
5924 arla_log(ADEBVLOG, "(%d, %u, %u, %u)" "%s%s%s%s"
5925 "%s%s%s%s" "%s%s%s%s" "%s%s%s%s" "%s%s%s usage: %llu",
5926 e->fid.Cell,
5927 e->fid.fid.Volume, e->fid.fid.Vnode, e->fid.fid.Unique,
5929 e->flags.usedp?" used":"",
5930 e->flags.attrp?" attr":"",
5931 e->usage != 0 ?" data":"",
5932 e->flags.attrusedp?" attrused":"",
5934 e->flags.datausedp?" dataused":"",
5935 e->flags.extradirp?" extradir":"",
5936 e->flags.mountp?" mount":"",
5937 e->flags.kernelp?" kernel":"",
5939 e->flags.sentenced?" sentenced":"",
5940 e->flags.stale?" stale":"",
5941 e->flags.dirtied?" dirtied":"",
5942 e->flags.silly?" silly":"",
5944 e->flags.fake_mp ? " fake mp" : "",
5945 e->flags.vol_root ? " vol root" : "",
5946 e->flags.waiters ? " waiters" : "",
5947 e->flags.gcp ? " gc" : "",
5949 e->flags.locked ? " locked" : "",
5950 e->flags.lockwait ? " lockwait" : "",
5951 e->flags.appended ? " appended" : "",
5952 (unsigned long long)e->usage);
5953 return FALSE;
5961 void
5962 fcache_status (void)
5964 arla_log(ADEBVLOG, "%lu (%lu-/%lu)-%lu) files"
5965 "%lu (%lu-%lu) bytes\n",
5966 usedvnodes, lowvnodes, current_vnodes, highvnodes,
5967 (long)usedbytes, (long)lowbytes, (long)highbytes);
5968 hashtabforeach (hashtab, print_entry, NULL);
5972 * Update cache usage of entry by adding `nblocks' times blocksize and
5973 * update accounting accordingly.
5976 static int
5977 fcache_update_usage(FCacheEntry *e, int nblocks)
5979 int64_t diff = ((int64_t)blocksize) * nblocks;
5980 int ret = 0;
5982 /* AssertExclLocked(&e->lock); */
5984 if (nblocks > 0)
5985 ret = fcache_need_bytes(diff);
5986 if (!ret) {
5987 usedbytes += diff;
5988 e->usage += diff;
5990 assert(e->usage <= usedbytes);
5991 assert(e->usage >= 0);
5994 return ret;
5998 * Mark the data range's presence in cache according to 'have'
5999 * Cache usage is updated, this may cause gc.
6003 fcache_set_have(FCacheEntry *entry, uint64_t offset, uint64_t end)
6005 uint64_t off;
6007 AssertExclLocked(&entry->lock);
6009 assert(offset <= end);
6011 off = block_offset(offset);
6012 do {
6013 if (!fcache_block_exists(entry, off)) {
6014 int ret = create_block(entry, off);
6015 if (ret)
6016 return ret;
6018 off += blocksize;
6019 } while (off < end);
6021 return 0;
6025 * Set new length of entry, and note it's all in cache
6028 void
6029 fcache_set_have_all(FCacheEntry *e, uint64_t len)
6031 AssertExclLocked(&e->lock);
6032 fcache_set_status_length(&e->status, len);
6036 * Request an ACL and put it in opaque
6040 getacl(VenusFid fid,
6041 CredCacheEntry *ce,
6042 AFSOpaque *opaque)
6044 FCacheEntry *dire;
6045 ConnCacheEntry *conn;
6046 AFSFetchStatus status;
6047 AFSVolSync volsync;
6048 int ret;
6049 fs_server_context context;
6051 opaque->val = NULL;
6052 opaque->len = 0;
6054 if (connected_mode != CONNECTED)
6055 return EINVAL;
6057 ret = fcache_get (&dire, fid, ce);
6058 if (ret) {
6059 arla_warn (ADEBFCACHE, ret, "fcache_get");
6060 return ret;
6063 ret = init_fs_context(dire, ce, &context);
6064 if (ret)
6065 return ret;
6067 for (conn = find_first_fs (&context);
6068 conn != NULL;
6069 conn = find_next_fs (&context, conn, ret)) {
6071 ret = RXAFS_FetchACL (conn->connection, &fid.fid,
6072 opaque, &status, &volsync);
6073 if (ret) {
6074 free(opaque->val);
6075 opaque->val = NULL;
6076 opaque->len = 0;
6079 if (!try_next_fs (ret, &fid))
6080 break;
6082 if (ret)
6083 arla_warn (ADEBFCACHE, ret, "FetchACL");
6085 if (ret == 0)
6086 update_entry (dire, &status, NULL, &volsync,
6087 conn, ce->cred);
6088 else if (host_downp(ret))
6089 ret = ENETDOWN;
6091 free_fs_server_context (&context);
6092 fcache_release (dire);
6093 return ret;
6097 * Store the ACL read from opaque
6099 * If the function return 0, ret_e is set to the dir-entry and must
6100 * be fcache_released().
6104 setacl(VenusFid fid,
6105 CredCacheEntry *ce,
6106 AFSOpaque *opaque,
6107 FCacheEntry **ret_e)
6109 FCacheEntry *dire;
6110 ConnCacheEntry *conn;
6111 AFSFetchStatus status;
6112 AFSVolSync volsync;
6113 int ret;
6114 fs_server_context context;
6116 if (connected_mode != CONNECTED)
6117 return EINVAL;
6119 ret = fcache_get (&dire, fid, ce);
6120 if (ret) {
6121 arla_warn (ADEBFCACHE, ret, "fcache_get");
6122 return EINVAL;
6125 ret = init_fs_context(dire, ce, &context);
6126 if (ret)
6127 return ret;
6129 for (conn = find_first_fs (&context);
6130 conn != NULL;
6131 conn = find_next_fs (&context, conn, ret)) {
6132 ret = RXAFS_StoreACL (conn->connection, &fid.fid,
6133 opaque, &status, &volsync);
6134 if (!try_next_fs (ret, &fid))
6135 break;
6137 if (ret)
6138 arla_warn (ADEBFCACHE, ret, "StoreACL");
6140 if (ret == 0)
6141 update_entry (dire, &status, NULL, &volsync,
6142 conn, ce->cred);
6143 else if (host_downp(ret))
6144 ret = ENETDOWN;
6146 free_fs_server_context (&context);
6148 if (ret == 0) {
6149 *ret_e = dire;
6150 } else {
6151 *ret_e = NULL;
6152 fcache_release (dire);
6154 return ret;
6158 * Request volume status
6162 getvolstat(VenusFid fid, CredCacheEntry *ce,
6163 AFSFetchVolumeStatus *volstat,
6164 char *volumename, size_t volumenamesz,
6165 char *offlinemsg,
6166 char *motd)
6168 FCacheEntry *dire;
6169 ConnCacheEntry *conn;
6170 int ret;
6171 fs_server_context context;
6173 if (connected_mode != CONNECTED)
6174 return EINVAL;
6176 ret = fcache_get (&dire, fid, ce);
6177 if (ret) {
6178 arla_warn (ADEBFCACHE, ret, "fcache_get");
6179 return EINVAL;
6182 ret = init_fs_context(dire, ce, &context);
6183 if (ret)
6184 return ret;
6186 for (conn = find_first_fs (&context);
6187 conn != NULL;
6188 conn = find_next_fs (&context, conn, ret)) {
6189 ret = RXAFS_GetVolumeStatus (conn->connection, fid.fid.Volume,
6190 volstat, volumename, offlinemsg,
6191 motd);
6192 if (!try_next_fs (ret, &fid))
6193 break;
6195 if (ret)
6196 arla_warn (ADEBFCACHE, ret, "GetVolumeStatus");
6197 free_fs_server_context (&context);
6198 if (host_downp(ret))
6199 ret = ENETDOWN;
6200 if (ret == 0 && volumename[0] == '\0') {
6201 if (volcache_getname (fid.fid.Volume, fid.Cell,
6202 volumename, volumenamesz) == -1)
6203 strlcpy(volumename, "<unknown>", volumenamesz);
6206 fcache_release (dire);
6207 return ret;
6211 * Store volume status
6215 setvolstat(VenusFid fid, CredCacheEntry *ce,
6216 AFSStoreVolumeStatus *volstat,
6217 char *volumename,
6218 char *offlinemsg,
6219 char *motd)
6221 FCacheEntry *dire;
6222 ConnCacheEntry *conn;
6223 int ret;
6224 fs_server_context context;
6226 if (connected_mode != CONNECTED)
6227 return EINVAL;
6229 ret = fcache_get (&dire, fid, ce);
6230 if (ret) {
6231 arla_warn (ADEBFCACHE, ret, "fcache_get");
6232 return EINVAL;
6235 ret = init_fs_context(dire, ce, &context);
6236 if (ret)
6237 return ret;
6239 for (conn = find_first_fs (&context);
6240 conn != NULL;
6241 conn = find_next_fs (&context, conn, ret)) {
6242 ret = RXAFS_SetVolumeStatus (conn->connection, fid.fid.Volume,
6243 volstat, volumename, offlinemsg,
6244 motd);
6245 if (!try_next_fs (ret, &fid))
6246 break;
6248 if (ret) {
6249 if (host_downp(ret))
6250 ret = ENETDOWN;
6251 arla_warn (ADEBFCACHE, ret, "SetVolumeStatus");
6253 free_fs_server_context (&context);
6255 fcache_release (dire);
6256 return ret;
6260 * Get `fbuf' from `centry'
6262 * Assume that data is valid and `centry' is exclusive locked.
6266 fcache_get_fbuf (FCacheEntry *centry, fbuf *fbuf, int fbuf_flags)
6268 uint64_t len;
6270 AssertExclLocked(&centry->lock);
6272 len = fcache_get_status_length(&centry->status);
6273 return abuf_create(fbuf, centry, len, fbuf_flags);
6280 static Bool
6281 sum_node (List *list, Listitem *li, void *arg)
6283 int64_t *a = arg;
6284 FCacheEntry *e = listdata (li);
6286 if (e != CLEANER_MARKER)
6287 *a += e->usage;
6289 return FALSE;
6293 int64_t
6294 fcache_calculate_usage (void)
6296 int64_t size = 0;
6298 listiter (kernel_node_lru, sum_node, &size);
6299 listiter (node_lru, sum_node, &size);
6301 return size;
6308 const VenusFid *
6309 fcache_realfid (const FCacheEntry *entry)
6311 if (entry->flags.vol_root
6312 || (entry->fid.fid.Vnode == 1 && entry->fid.fid.Unique == 1)) {
6313 long voltype = getvoltype(entry->fid.fid.Volume, entry->volume);
6314 return &entry->volume->parent[voltype].mp_fid;
6315 } else {
6316 return &entry->fid;
6324 static Bool
6325 check_dir (List *list, Listitem *li, void *arg)
6327 FCacheEntry *e = listdata (li);
6328 fbuf the_fbuf;
6329 uint64_t len;
6330 int ret;
6332 if (e == CLEANER_MARKER)
6333 return FALSE;
6335 if (fcache_islocked(e))
6336 return FALSE;
6338 fcache_lock(e, TRUE);
6340 len = fcache_get_status_length(&e->status);
6341 if (!fcache_have_wanted(e, 0, len))
6342 goto out;
6344 ret = fcache_get_fbuf(e, &the_fbuf, FBUF_READ);
6345 if (ret)
6346 goto out;
6348 ret = fdir_dirp(&the_fbuf);
6350 abuf_end (&the_fbuf);
6352 if (e->status.FileType == TYPE_DIR)
6353 assert(ret);
6354 else
6355 assert(!ret);
6357 out:
6358 fcache_unlock(e);
6360 return FALSE;
6364 * Verifies that directories seems to be directories and files doesn't
6365 * seems to be directories, note that false positives are
6366 * possible in the latter of these cases, so this should not be turned on
6367 * default.
6370 void
6371 fcache_check_dirs(void)
6373 listiter (node_lru, check_dir, NULL);
6374 listiter (kernel_node_lru, check_dir, NULL);
6377 struct check_block_arg {
6378 Listitem *prev;
6382 * As much paranoia as possible.
6385 static Bool
6386 check_block(List *list, Listitem *li, void *arg)
6388 struct check_block_arg *cba = (struct check_block_arg *)arg;
6389 struct block *b = (struct block *)listdata(li);
6391 if (b != CLEANER_MARKER) {
6392 assert(b->lru_le == li);
6393 assert(listprev(list, li) == cba->prev);
6394 assert(block_offset(b->offset) == b->offset);
6395 assert(b->node->flags.usedp);
6396 assert(!block_emptyp(b->node));
6398 if (list == kernel_block_lru) {
6399 assert(b->flags.kernelp);
6400 assert(b->node->flags.kernelp);
6401 } else if (list == block_lru) {
6402 assert(!b->flags.kernelp);
6403 } else {
6404 assert(0);
6408 cba->prev = li;
6410 return FALSE;
6414 * Verify that the block lists are consistent.
6417 void
6418 fcache_check_blocks(void)
6420 struct check_block_arg arg;
6422 arg.prev = NULL;
6423 listiter(block_lru, check_block, &arg);
6425 arg.prev = NULL;
6426 listiter(kernel_block_lru, check_block, &arg);