*** empty log message ***
[arla.git] / arlad / fcache.c
blob8daf3b9c02f9e2a41057f8cb278446335f0bb2f6
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 #ifdef __CYGWIN32__
43 #include <windows.h>
44 #endif
47 * Prototypes
50 static int get_attr_bulk (FCacheEntry *parent_entry,
51 FCacheEntry *prefered_entry,
52 VenusFid *prefered_fid,
53 const char *prefered_name,
54 CredCacheEntry *ce);
56 static int
57 resolve_mp (FCacheEntry **e, VenusFid *ret_fid, CredCacheEntry **ce);
60 * Local data for this module.
64 * Hash table for all the vnodes known by the cache manager keyed by
65 * (cell, volume, vnode, unique).
68 static Hashtab *hashtab;
71 * List of all hash table entries. This list is sorted in LRU-order.
72 * The head is the MRU and the tail the LRU, which is from where we
73 * take entries when we need to add new ones.
76 static List *lrulist;
79 * Heap of entries to be invalidated.
82 static Heap *invalid_heap;
84 /* low and high-water marks for vnodes and space */
86 static u_long highvnodes, lowvnodes, current_vnodes;
87 static int64_t highbytes, lowbytes;
89 /* current values */
91 static int64_t usedbytes, needbytes;
92 static u_long usedvnodes;
94 /* Map with recovered nodes */
96 static u_long maxrecovered;
98 static char *recovered_map;
100 static void
101 set_recovered(u_long index)
103 char *p;
104 u_long oldmax;
106 if (index >= maxrecovered) {
107 oldmax = maxrecovered;
108 maxrecovered = (index + 16) * 2;
109 p = realloc(recovered_map, maxrecovered);
110 if (p == NULL) {
111 arla_errx(1, ADEBERROR, "fcache: realloc %lu recovered_map failed",
112 maxrecovered);
114 recovered_map = p;
115 memset(recovered_map + oldmax, 0, maxrecovered - oldmax);
117 recovered_map[index] = 1;
120 #define IS_RECOVERED(index) (recovered_map[(index)])
123 * This is how far the cleaner will go to clean out entries.
124 * The higher this is, the higher the risk is that you will
125 * loose any file that you feel is important to disconnected
126 * operation.
129 Bool fprioritylevel = arla_FPRIO_DEFAULT;
131 static int node_count; /* XXX */
134 * This is set to non-zero when we want to use bulkstatus(). 2 means
135 * that the nodes should be installed into the kernel.
138 static int fcache_enable_bulkstatus = 1;
139 static int fcache_bulkstatus_num = 14; /* XXX should use the [P]MTU */
141 #define FCHASHSIZE 997
144 * The cleaner
147 #define CLEANER_SLEEP 10
149 static PROCESS cleaner_pid;
152 * The creator of nodes.
155 static PROCESS create_nodes_pid;
158 * The invalidator
161 static PROCESS invalidator_pid;
164 * Smalltalk emulation
167 int64_t
168 fcache_highbytes(void)
170 return highbytes;
173 int64_t
174 fcache_usedbytes(void)
176 return usedbytes;
179 int64_t
180 fcache_lowbytes(void)
182 return lowbytes;
185 u_long
186 fcache_highvnodes(void)
188 return highvnodes;
191 u_long
192 fcache_usedvnodes(void)
194 return usedvnodes;
197 u_long
198 fcache_lowvnodes(void)
200 return lowvnodes;
204 * Counters
207 static struct {
208 unsigned long fetch_attr;
209 unsigned long fetch_attr_cached;
210 unsigned long fetch_attr_bulk;
211 unsigned long fetch_data;
212 unsigned long fetch_data_cached;
213 unsigned long store_attr;
214 unsigned long store_data;
215 } fcache_counter;
218 * Compare two entries. Return 0 if and only if the same.
221 static int
222 fcachecmp (void *a, void *b)
224 FCacheEntry *f1 = (FCacheEntry*)a;
225 FCacheEntry *f2 = (FCacheEntry*)b;
227 return VenusFid_cmp(&f1->fid, &f2->fid);
231 * Hash the value of an entry.
234 static unsigned
235 fcachehash (void *e)
237 FCacheEntry *f = (FCacheEntry*)e;
239 return f->fid.Cell + f->fid.fid.Volume + f->fid.fid.Vnode
240 + f->fid.fid.Unique;
244 * Compare expiration times.
247 static int
248 expiration_time_cmp (const void *a, const void *b)
250 const FCacheEntry *f1 = (const FCacheEntry *)a;
251 const FCacheEntry *f2 = (const FCacheEntry *)b;
253 return f1->callback.ExpirationTime - f2->callback.ExpirationTime;
256 void
257 recon_hashtabadd(FCacheEntry *entry)
259 hashtabadd(hashtab,entry);
262 void
263 recon_hashtabdel(FCacheEntry *entry)
265 hashtabdel(hashtab,entry);
269 * Globalnames
272 char **sysnamelist = NULL;
273 int sysnamenum = 0;
279 static void
280 fcache_poller_unref(FCacheEntry *e)
282 AssertExclLocked(&e->lock);
284 if (e->poll) {
285 poller_remove(e->poll);
286 e->poll = NULL;
290 static void
291 fcache_poller_reref(FCacheEntry *e, ConnCacheEntry *conn)
293 PollerEntry *pe = e->poll;
294 AssertExclLocked(&e->lock);
296 e->poll = poller_add_conn(conn);
297 if (pe)
298 poller_remove(pe);
306 const char *
307 fcache_getdefsysname (void)
309 if (sysnamenum == 0)
310 return "fool-dont-remove-all-sysnames";
311 return sysnamelist[0];
319 fcache_setdefsysname (const char *sysname)
321 if (sysnamenum == 0)
322 return fcache_addsysname (sysname);
323 free (sysnamelist[0]);
324 sysnamelist[0] = estrdup (sysname);
325 return 0;
333 fcache_addsysname (const char *sysname)
335 sysnamenum += 1;
336 sysnamelist = erealloc (sysnamelist,
337 sysnamenum * sizeof(char *));
338 sysnamelist[sysnamenum - 1] = estrdup(sysname);
339 return 0;
347 fcache_removesysname (const char *sysname)
349 int i;
350 for (i = 0; i < sysnamenum; i++)
351 if (strcmp (sysnamelist[i], sysname) == 0)
352 break;
353 if (i == sysnamenum)
354 return 1;
355 free (sysnamelist[i]);
356 for (;i < sysnamenum; i++)
357 sysnamelist[i] = sysnamelist[i + 1];
358 sysnamenum--;
359 sysnamelist = erealloc (sysnamelist,
360 sysnamenum * sizeof(char *));
361 return 0;
365 * return the directory name of the cached file for `entry'
369 fcache_dir_name (FCacheEntry *entry, char *s, size_t len)
371 return snprintf (s, len, "%02X", entry->index / 0x100);
375 * return the file name of the cached file for `entry'.
379 fcache_file_name (FCacheEntry *entry, char *s, size_t len)
381 return snprintf (s, len, "%02X/%02X",
382 entry->index / 0x100, entry->index % 0x100);
386 * return kernel version of path to the cache file for `entry'.
390 fcache_conv_file_name (FCacheEntry *entry, char *s, size_t len)
392 #ifdef __CYGWIN32__
393 char buf[1024];
394 GetCurrentDirectory(1024, buf);
396 return snprintf (s, len, "%s\\%02X\\%02X",
397 buf, entry->index / 0x100, entry->index % 0x100);
398 #else
399 return snprintf (s, len, "%02X/%02X",
400 entry->index / 0x100, entry->index % 0x100);
401 #endif
405 * the filename for the extra (converted) directory
408 static int
409 real_extra_file_name (FCacheEntry *entry, char *s, size_t len)
411 int ret;
413 ret = fcache_file_name (entry, s, len - 1);
414 if (ret < len - 1) {
415 s[ret++] = '@';
416 s[ret] = '\0';
418 return ret;
422 * return the file name of the converted directory for `entry'.
426 fcache_extra_file_name (FCacheEntry *entry, char *s, size_t len)
428 assert (entry->flags.extradirp &&
429 entry->status.FileType == TYPE_DIR);
431 return real_extra_file_name (entry, s, len);
434 static int fhopen_working;
437 * open file by handle
440 static int
441 fcache_fhopen (fcache_cache_handle *handle, int flags)
443 if (!handle->valid) {
444 errno = EINVAL;
445 return -1;
448 #ifdef __CYGWIN32__
449 return -1;
450 #endif
452 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
454 int ret;
455 fhandle_t fh;
457 memcpy (&fh, &handle->nnpfs_handle, sizeof(fh));
458 ret = fhopen (&fh, flags);
459 if (ret >= 0)
460 return ret;
462 #endif
464 #ifdef KERBEROS /* really KAFS */
466 struct arlaViceIoctl vice_ioctl;
468 vice_ioctl.in = (caddr_t)&handle->nnpfs_handle;
469 vice_ioctl.in_size = sizeof(handle->nnpfs_handle);
471 vice_ioctl.out = NULL;
472 vice_ioctl.out_size = 0;
474 return k_pioctl (NULL, ARLA_VIOC_FHOPEN, (void *)&vice_ioctl, flags);
476 #else
477 errno = EINVAL;
478 return -1;
479 #endif
483 * get the handle of `filename'
487 fcache_fhget (char *filename, fcache_cache_handle *handle)
489 handle->valid = 0;
491 #ifdef __CYGWIN32__
493 int ret, a, b;
494 char buf[1024];
496 ret = sscanf(filename, "%02X/%02X", &a, &b);
497 if (ret != 2)
498 return EINVAL;
500 GetCurrentDirectory(sizeof(buf)-1, buf);
501 buf[sizeof(buf) - 1] = '\0';
503 ret = snprintf((char *)handle->nnpfs_handle,
504 sizeof(handle->nnpfs_handle),
505 "%s\\%02X\\%02X", buf, a, b);
507 if (ret > 0 || ret < sizeof(handle->nnpfs_handle))
508 handle->valid = 1;
510 return ret;
512 #endif
514 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
516 int ret;
517 fhandle_t fh;
519 ret = getfh (filename, &fh);
520 if (ret == 0) {
521 memcpy (&handle->nnpfs_handle, &fh, sizeof(fh));
522 handle->valid = 1;
525 return ret;
527 #endif
528 #ifdef KERBEROS
530 struct arlaViceIoctl vice_ioctl;
531 int ret;
533 if (!fhopen_working)
534 return 0;
536 vice_ioctl.in = NULL;
537 vice_ioctl.in_size = 0;
539 vice_ioctl.out = (caddr_t)&handle->nnpfs_handle;
540 vice_ioctl.out_size = sizeof(handle->nnpfs_handle);
542 ret = k_pioctl (filename, ARLA_VIOC_FHGET, (void *)&vice_ioctl, 0);
543 if (ret == 0)
544 handle->valid = 1;
546 return ret;
548 #else
549 errno = EINVAL;
550 return -1;
551 #endif
555 * create a new cache vnode, assume the entry is locked or private
558 static int
559 fcache_create_file (FCacheEntry *entry, int create)
561 char fname[MAXPATHLEN];
562 char extra_fname[MAXPATHLEN];
563 int fd;
564 int ret;
565 int flags;
566 int largefileflag = 0;
568 if (use_o_largefile)
569 largefileflag = O_LARGEFILE;
571 flags = O_RDWR | O_BINARY | largefileflag;
573 if (create)
574 flags |= O_CREAT | O_TRUNC;
576 fcache_file_name (entry, fname, sizeof(fname));
577 fd = open (fname, flags, 0666);
578 if (fd < 0) {
579 if (errno == ENOENT && create) {
580 char dname[MAXPATHLEN];
582 fcache_dir_name (entry, dname, sizeof(dname));
583 ret = mkdir (dname, 0777);
584 if (ret < 0)
585 arla_err (1, ADEBERROR, errno, "mkdir %s", dname);
586 fd = open (fname, flags, 0666);
587 if (fd < 0)
588 arla_err (1, ADEBERROR, errno, "open %s", fname);
589 } else {
590 arla_err (1, ADEBERROR, errno, "open %s", fname);
593 if (close (fd) < 0)
594 arla_err (1, ADEBERROR, errno, "close %s", fname);
595 fcache_fhget (fname, &entry->handle);
596 real_extra_file_name (entry, extra_fname, sizeof(extra_fname));
597 unlink (extra_fname);
598 return 0;
602 * return a fd to the cache file of `entry'
606 fcache_open_file (FCacheEntry *entry, int flag)
608 int ret;
609 char fname[MAXPATHLEN];
610 int largefileflag = 0;
612 if (use_o_largefile)
613 largefileflag = O_LARGEFILE;
615 if (fhopen_working) {
616 ret = fcache_fhopen (&entry->handle, flag | largefileflag);
617 if (ret < 0 && (errno == EINVAL || errno == EPERM))
618 fhopen_working = 0;
619 else
620 return ret;
622 fcache_file_name (entry, fname, sizeof(fname));
623 return open (fname, flag | O_BINARY | largefileflag);
627 * return a fd to the converted directory for `entry'
631 fcache_open_extra_dir (FCacheEntry *entry, int flag, mode_t mode)
633 char fname[MAXPATHLEN];
635 assert (entry->flags.extradirp &&
636 entry->status.FileType == TYPE_DIR);
638 fcache_extra_file_name (entry, fname, sizeof(fname));
639 return open (fname, flag | O_BINARY, mode);
646 uint64_t
647 fcache_get_status_length(const AFSFetchStatus *status)
649 return status->Length | ((uint64_t)status->LengthHigh << 32);
652 static void
653 fcache_set_status_length(AFSFetchStatus *status, int64_t length)
655 status->Length = length & 0xffffffff;
656 status->LengthHigh = length >> 32;
661 * Discard the data cached for `entry'.
664 static void
665 throw_data (FCacheEntry *entry)
667 int fd;
668 struct stat sb;
670 assert (entry->flags.usedp);
671 AssertExclLocked(&entry->lock);
673 fd = fcache_open_file (entry, O_WRONLY);
674 if (fd < 0) {
675 arla_warn (ADEBFCACHE, errno, "fcache_open_file");
676 goto out;
678 if (fstat (fd, &sb) < 0) {
679 arla_warn (ADEBFCACHE, errno, "fstat");
680 close (fd);
681 goto out;
683 if (ftruncate (fd, 0) < 0) {
684 arla_warn (ADEBFCACHE, errno, "ftruncate");
685 close (fd);
686 goto out;
688 close (fd);
689 if (entry->flags.extradirp) {
690 char fname[MAXPATHLEN];
692 fcache_extra_file_name (entry, fname, sizeof(fname));
693 unlink (fname);
695 assert(usedbytes >= entry->length);
696 /* XXX - things are wrong - continue anyway */
697 if (usedbytes < entry->length)
698 usedbytes = entry->length;
699 usedbytes -= entry->length;
700 entry->length = 0;
701 entry->wanted_length = 0;
702 entry->fetched_length = 0;
703 entry->flags.extradirp = FALSE;
705 out:
706 cm_check_consistency();
710 * A probe function for a file server.
714 fs_probe (struct rx_connection *conn)
716 uint32_t sec, usec;
718 return RXAFS_GetTime (conn, &sec, &usec);
725 static void
726 throw_entry (FCacheEntry *entry)
728 CredCacheEntry *ce;
729 ConnCacheEntry *conn;
730 AFSCBFids fids;
731 AFSCBs cbs;
732 int ret;
734 assert (entry->flags.usedp);
735 assert (!entry->flags.kernelp);
737 AssertExclLocked(&entry->lock);
738 assert(LockWaiters(&entry->lock) == 0);
740 hashtabdel (hashtab, entry);
743 * Throw data when there is any, length is a good test since the
744 * node must not be used (in kernel) when we get here.
746 if (entry->length)
747 throw_data (entry);
749 if (entry->invalid_ptr != -1) {
750 heap_remove (invalid_heap, entry->invalid_ptr);
751 entry->invalid_ptr = -1;
754 fcache_poller_unref(entry);
756 if (entry->flags.attrp && !entry->flags.silly && entry->host) {
757 ce = cred_get (entry->fid.Cell, 0, CRED_NONE);
758 assert (ce != NULL);
760 conn = conn_get (entry->fid.Cell, entry->host, afsport,
761 FS_SERVICE_ID, fs_probe, ce);
762 cred_free (ce);
764 fids.len = cbs.len = 1;
765 fids.val = &entry->fid.fid;
766 cbs.val = &entry->callback;
768 if (conn_isalivep (conn)) {
769 ret = RXAFS_GiveUpCallBacks(conn->connection, &fids, &cbs);
770 if (host_downp(ret)) {
771 conn_dead (conn);
772 ret = ENETDOWN;
774 } else
775 ret = ENETDOWN;
776 conn_free (conn);
777 if (ret)
778 arla_warn (ADEBFCACHE, ret, "RXAFS_GiveUpCallBacks");
780 if (entry->volume) {
781 volcache_free (entry->volume);
782 entry->volume = NULL;
784 assert_not_flag(entry,kernelp);
785 entry->flags.attrp = FALSE;
786 entry->flags.usedp = FALSE;
787 --usedvnodes;
788 LWP_NoYieldSignal (lrulist);
792 * Return the next cache node number.
795 static unsigned
796 next_cache_index (void)
798 do {
799 node_count++;
800 } while ((node_count < maxrecovered)
801 && IS_RECOVERED(node_count));
803 return node_count;
807 * Pre-create cache nodes up to the limit highvnodes. If you want to
808 * create more increase highnodes and signal create_nodes.
811 static void
812 create_nodes (char *arg)
814 FCacheEntry *entries;
815 unsigned count = 0;
816 struct timeval tv;
818 while (1) {
819 unsigned int n, i, j;
821 while (highvnodes <= current_vnodes)
822 LWP_WaitProcess (create_nodes);
824 n = highvnodes - current_vnodes;
826 count = 0;
828 arla_warnx (ADEBFCACHE,
829 "pre-creating nodes");
831 entries = calloc (n, sizeof(FCacheEntry));
832 if (n != 0 && entries == NULL)
833 arla_errx (1, ADEBERROR, "fcache: calloc failed");
835 for (i = 0; i < n; ++i) {
836 entries[i].invalid_ptr = -1;
837 entries[i].volume = NULL;
838 entries[i].refcount = 0;
839 entries[i].anonaccess = 0;
840 entries[i].cleanergen = 0;
841 entries[i].poll = NULL;
842 for (j = 0; j < NACCESS; j++) {
843 entries[i].acccache[j].cred = ARLA_NO_AUTH_CRED;
844 entries[i].acccache[j].access = 0;
846 entries[i].length = 0;
847 Lock_Init(&entries[i].lock);
848 entries[i].index = next_cache_index ();
849 fcache_create_file (&entries[i], 1);
851 current_vnodes++;
853 ++count;
854 tv.tv_sec = 0;
855 tv.tv_usec = 1000;
857 entries[i].lru_le = listaddhead (lrulist, &entries[i]);
858 assert (entries[i].lru_le);
860 LWP_NoYieldSignal (lrulist);
861 IOMGR_Select(0, NULL, NULL, NULL, &tv);
864 arla_warnx (ADEBFCACHE,
865 "pre-created %u nodes", count);
870 * This is the almighty cleaner loop
873 static Bool cleaner_working = FALSE;
875 static void
876 cleaner (char *arg)
878 enum { CL_OPPORTUNISTIC, CL_FORCE, CL_COLLECT } state;
879 int cnt = 0, numnodes;
880 VenusFid *fids;
881 int cleanerrun = 0;
883 numnodes = NNPFS_GC_NODES_MAX_HANDLE;
885 fids = malloc (sizeof(*fids) * numnodes);
886 if (fids == NULL)
887 arla_err (1, ADEBERROR, errno, "cleaner: malloc");
889 for (;;) {
890 Listitem *item, *prev;
891 FCacheEntry *entry;
893 arla_warnx (ADEBCLEANER,
894 "running cleaner: "
895 "%lu (%lu-(%lu)-%lu) files, "
896 "%lu (%lu-%lu) bytes "
897 "%lu needed bytes",
898 usedvnodes, lowvnodes, current_vnodes, highvnodes,
899 (long)usedbytes, (long)lowbytes, (long)highbytes,
900 (long)needbytes);
902 cleaner_working = TRUE;
904 state = CL_OPPORTUNISTIC;
905 cleanerrun++;
907 while (usedvnodes > lowvnodes
908 || usedbytes > lowbytes
909 || needbytes > highbytes - usedbytes)
912 for (item = listtail (lrulist);
913 item &&
914 (usedvnodes > lowvnodes
915 || usedbytes > lowbytes
916 || needbytes > highbytes - usedbytes);
917 item = prev) {
918 prev = listprev (lrulist, item);
919 entry = (FCacheEntry *)listdata (item);
921 if (fprioritylevel && entry->priority)
922 continue;
924 if (entry->cleanergen == cleanerrun)
925 continue;
926 entry->cleanergen = cleanerrun;
928 if (entry->flags.usedp
929 && (usedvnodes > lowvnodes
930 || usedbytes > lowbytes
931 || needbytes > highbytes - usedbytes)
932 && entry->refcount == 0
933 && CheckLock(&entry->lock) == 0)
935 if (!entry->flags.datausedp
936 && !entry->flags.kernelp
937 /* && this_is_a_good_node_to_gc(entry,state) */) {
939 ObtainWriteLock (&entry->lock);
940 listdel (lrulist, item);
941 throw_entry (entry);
942 entry->lru_le = listaddtail (lrulist, entry);
943 assert(entry->lru_le);
944 ReleaseWriteLock (&entry->lock);
945 break;
948 if (state == CL_FORCE && entry->flags.kernelp) {
950 fids[cnt++] = entry->fid;
952 if (cnt >= numnodes) {
953 nnpfs_send_message_gc_nodes (kernel_fd, cnt, fids);
954 IOMGR_Poll();
955 cnt = 0;
957 break;
960 assert (entry->lru_le == item);
962 if (item == NULL) {
963 switch (state) {
964 case CL_OPPORTUNISTIC:
965 state = CL_FORCE;
966 LWP_DispatchProcess(); /* Yield */
967 break;
968 case CL_FORCE:
969 state = CL_COLLECT;
970 if (cnt > 0) {
971 nnpfs_send_message_gc_nodes (kernel_fd, cnt, fids);
972 IOMGR_Poll();
973 cnt = 0;
975 break;
976 case CL_COLLECT:
977 goto out;
978 break;
979 default:
980 abort();
982 cleanerrun++;
985 out:
987 arla_warnx(ADEBCLEANER,
988 "cleaner done: "
989 "%lu (%lu-(%lu)-%lu) files, "
990 "%ld (%ld-%ld) bytes "
991 "%ld needed bytes",
992 usedvnodes, lowvnodes, current_vnodes, highvnodes,
993 (long)usedbytes, (long)lowbytes, (long)highbytes,
994 (long)needbytes);
996 cm_check_consistency();
997 if (needbytes)
998 LWP_NoYieldSignal (fcache_need_bytes);
999 cleaner_working = FALSE;
1000 IOMGR_Sleep (CLEANER_SLEEP);
1004 static void
1005 fcache_wakeup_cleaner (void *wait)
1007 if (cleaner_working == FALSE)
1008 IOMGR_Cancel (cleaner_pid);
1009 LWP_WaitProcess (wait);
1013 fcache_need_bytes (u_long needed)
1015 if (needed + needbytes > highbytes) {
1016 arla_warnx (ADEBWARN,
1017 "Out of space since there is outstanding requests "
1018 "(%ld needed, %ld outstanding, %ld highbytes)",
1019 (long)needed, (long)needbytes, (long)highbytes);
1020 return ENOSPC;
1023 needbytes += needed;
1024 fcache_wakeup_cleaner(fcache_need_bytes);
1025 needbytes -= needed;
1026 if (needed > highbytes - usedbytes) {
1027 arla_warnx (ADEBWARN,
1028 "Out of space, couldn't get needed bytes after cleaner "
1029 "(%lu bytes missing, %lu used, %lu highbytes)",
1030 (long)(needed - (highbytes - usedbytes)),
1031 (long)usedbytes, (long)highbytes);
1032 return ENOSPC;
1034 return 0;
1037 Bool
1038 fcache_need_nodes (void)
1040 fcache_wakeup_cleaner (lrulist);
1041 if (current_vnodes == usedvnodes)
1042 return FALSE;
1043 return TRUE;
1048 * Run through the heap of objects to be invalidated and throw them away
1049 * when their expirationtime arrive.
1052 static void
1053 invalidator (char *arg)
1055 for (;;) {
1056 const void *head;
1057 struct timeval tv;
1059 arla_warnx(ADEBINVALIDATOR,
1060 "running invalidator");
1062 while ((head = heap_head (invalid_heap)) == NULL)
1063 LWP_WaitProcess (invalid_heap);
1065 gettimeofday (&tv, NULL);
1067 while ((head = heap_head (invalid_heap)) != NULL) {
1068 FCacheEntry *entry = (FCacheEntry *)head;
1070 if (tv.tv_sec < entry->callback.ExpirationTime) {
1071 unsigned long t = entry->callback.ExpirationTime - tv.tv_sec;
1073 arla_warnx (ADEBINVALIDATOR,
1074 "invalidator: sleeping for %lu second(s)", t);
1075 IOMGR_Sleep (t);
1076 break;
1079 ObtainWriteLock (&entry->lock);
1080 if (head == heap_head (invalid_heap)) {
1081 heap_remove_head (invalid_heap);
1082 entry->invalid_ptr = -1;
1083 if (entry->flags.kernelp)
1084 break_callback (entry);
1085 fcache_poller_unref(entry);
1087 ReleaseWriteLock (&entry->lock);
1093 * Add `entry' to the list of entries to invalidate when its time is
1094 * up.
1097 static void
1098 add_to_invalidate (FCacheEntry *e)
1100 if (e->invalid_ptr != -1)
1101 heap_remove (invalid_heap, e->invalid_ptr);
1102 heap_insert (invalid_heap, (const void *)e, &e->invalid_ptr);
1103 LWP_NoYieldSignal (invalid_heap);
1104 IOMGR_Cancel(invalidator_pid);
1108 * Remove the entry least-recently used and return it locked. Sleep until
1109 * there is an entry.
1112 static FCacheEntry *
1113 unlink_lru_entry (void)
1115 FCacheEntry *entry = NULL;
1116 Listitem *item;
1118 if (current_vnodes == usedvnodes)
1119 fcache_need_nodes();
1121 for (;;) {
1123 assert (!listemptyp (lrulist));
1124 for (item = listtail (lrulist);
1125 item;
1126 item = listprev (lrulist, item)) {
1128 entry = (FCacheEntry *)listdata (item);
1129 if (!entry->flags.usedp
1130 && CheckLock(&entry->lock) == 0) {
1131 assert_not_flag(entry,kernelp);
1132 ObtainWriteLock (&entry->lock);
1133 listdel (lrulist, entry->lru_le);
1134 entry->lru_le = NULL;
1135 return entry;
1139 assert (!listemptyp (lrulist));
1140 for (item = listtail (lrulist);
1141 item;
1142 item = listprev (lrulist, item)) {
1144 entry = (FCacheEntry *)listdata (item);
1145 if (entry->flags.usedp
1146 && !entry->flags.attrusedp
1147 && !entry->flags.kernelp
1148 && entry->refcount == 0
1149 && CheckLock(&entry->lock) == 0) {
1150 assert_not_flag(entry,kernelp);
1151 ObtainWriteLock (&entry->lock);
1152 listdel (lrulist, entry->lru_le);
1153 entry->lru_le = NULL;
1154 throw_entry (entry);
1155 return entry;
1159 arla_warnx (ADEBFCACHE, "unlink_lru_entry: sleeping");
1160 fcache_need_nodes();
1165 * Return a usable locked entry.
1168 static FCacheEntry *
1169 find_free_entry (void)
1171 FCacheEntry *entry;
1173 entry = unlink_lru_entry ();
1174 if (entry == NULL)
1175 arla_warnx (ADEBWARN, "All vnode entries in use");
1176 else {
1177 AssertExclLocked(&entry->lock);
1178 ++usedvnodes;
1180 return entry;
1187 struct fstore_context {
1188 Listitem *item;
1189 unsigned n;
1192 static int
1193 fcache_store_entry (struct fcache_store *st, void *ptr)
1195 struct fstore_context *c;
1196 FCacheEntry *entry;
1198 c = (struct fstore_context *)ptr;
1199 if (c->item == NULL) /* check if done ? */
1200 return STORE_DONE;
1202 entry = (FCacheEntry *)listdata (c->item);
1203 c->item = listprev (lrulist, c->item);
1205 if (!entry->flags.usedp)
1206 return STORE_SKIP;
1208 strlcpy(st->cell, cell_num2name(entry->fid.Cell), sizeof(st->cell));
1209 st->fid = entry->fid.fid;
1210 st->refcount = entry->refcount;
1211 st->length = entry->length;
1212 st->fetched_length = entry->fetched_length;
1213 st->volsync = entry->volsync;
1214 st->status = entry->status;
1215 st->anonaccess = entry->anonaccess;
1216 st->index = entry->index;
1217 st->flags.attrp = entry->flags.attrp;
1218 st->flags.datap = entry->length ? TRUE : FALSE;
1219 st->flags.extradirp = entry->flags.extradirp;
1220 st->flags.mountp = entry->flags.mountp;
1221 st->flags.fake_mp = entry->flags.fake_mp;
1222 st->flags.vol_root = entry->flags.vol_root;
1223 strlcpy(st->parentcell, cell_num2name(entry->parent.Cell),
1224 sizeof(st->parentcell));
1225 st->parent = entry->parent.fid;
1226 st->priority = entry->priority;
1228 c->n++;
1229 return STORE_NEXT;
1237 fcache_store_state (void)
1239 struct fstore_context c;
1240 int ret;
1242 if (lrulist == NULL) {
1243 arla_warnx (ADEBFCACHE, "store_state: lrulist is NULL\n");
1244 return 0;
1247 c.item = listtail(lrulist);
1248 c.n = 0;
1250 ret = state_store_fcache("fcache", fcache_store_entry, &c);
1251 if (ret)
1252 arla_warn(ADEBWARN, ret, "failed to write fcache state");
1253 else
1254 arla_warnx (ADEBFCACHE, "wrote %u entries to fcache", c.n);
1256 return 0;
1263 static int
1264 fcache_recover_entry (struct fcache_store *st, void *ptr)
1266 AFSCallBack broken_callback = {0, 0, CBDROPPED};
1267 unsigned *n = (unsigned *)ptr;
1269 CredCacheEntry *ce;
1270 FCacheEntry *e;
1271 int i;
1272 VolCacheEntry *vol;
1273 int res;
1274 int32_t cellid;
1276 cellid = cell_name2num(st->cell);
1277 assert (cellid != -1);
1279 ce = cred_get (cellid, 0, 0);
1280 assert (ce != NULL);
1282 res = volcache_getbyid (st->fid.Volume, cellid, ce, &vol, NULL);
1283 cred_free (ce);
1284 if (res)
1285 return 0;
1286 assert(vol);
1288 e = calloc(1, sizeof(FCacheEntry));
1289 e->invalid_ptr = -1;
1290 Lock_Init(&e->lock);
1291 ObtainWriteLock(&e->lock);
1294 e->fid.Cell = cellid;
1295 e->fid.fid = st->fid;
1296 e->host = 0;
1297 e->status = st->status;
1298 e->length = st->length;
1299 e->fetched_length = st->fetched_length;
1300 e->callback = broken_callback;
1301 e->volsync = st->volsync;
1302 e->refcount = st->refcount;
1304 /* Better not restore the rights. pags don't have to be the same */
1305 for (i = 0; i < NACCESS; ++i) {
1306 e->acccache[i].cred = ARLA_NO_AUTH_CRED;
1307 e->acccache[i].access = ANONE;
1310 e->anonaccess = st->anonaccess;
1311 e->index = st->index;
1312 fcache_create_file(e, 0);
1313 set_recovered(e->index);
1314 e->flags.usedp = TRUE;
1315 e->flags.attrp = st->flags.attrp;
1316 /* st->flags.datap */
1317 e->flags.attrusedp = FALSE;
1318 e->flags.datausedp = FALSE;
1319 e->flags.kernelp = FALSE;
1320 e->flags.extradirp = st->flags.extradirp;
1321 e->flags.mountp = st->flags.mountp;
1322 e->flags.fake_mp = st->flags.fake_mp;
1323 e->flags.vol_root = st->flags.vol_root;
1324 e->flags.sentenced = FALSE;
1325 e->flags.silly = FALSE;
1326 e->tokens = 0;
1327 e->parent.Cell = cell_name2num(st->parentcell);
1328 assert(e->parent.Cell != -1);
1329 e->parent.fid = st->parent;
1330 e->priority = st->priority;
1331 e->hits = 0;
1332 e->cleanergen = 0;
1333 e->lru_le = listaddhead (lrulist, e);
1334 assert(e->lru_le);
1335 e->volume = vol;
1336 hashtabadd (hashtab, e);
1337 if (e->length)
1338 usedbytes += e->length;
1339 ReleaseWriteLock (&e->lock);
1341 (*n)++;
1343 return 0;
1350 static void
1351 fcache_recover_state (void)
1353 unsigned n;
1355 n = 0;
1356 state_recover_fcache("fcache", fcache_recover_entry, &n);
1358 arla_warnx (ADEBFCACHE, "recovered %u entries to fcache", n);
1359 current_vnodes = n;
1363 * Search for `cred' in `ae' and return a pointer in `pos'. If it
1364 * already exists return TRUE, else return FALSE and set pos to a
1365 * random slot.
1368 Bool
1369 findaccess (nnpfs_pag_t cred, AccessEntry *ae, AccessEntry **pos)
1371 int i;
1373 for(i = 0; i < NACCESS ; ++i)
1374 if(ae[i].cred == cred) {
1375 *pos = &ae[i];
1376 return TRUE;
1379 i = rand() % NACCESS;
1380 *pos = &ae[i];
1381 return FALSE;
1389 static int
1390 fs_rtt_cmp (const void *v1, const void *v2)
1392 struct fs_server_entry *e1 = (struct fs_server_entry *)v1;
1393 struct fs_server_entry *e2 = (struct fs_server_entry *)v2;
1395 return conn_rtt_cmp(&e1->conn, &e2->conn);
1399 * Initialize a `fs_server_context'.
1402 static void
1403 init_fs_server_context (fs_server_context *context)
1405 context->num_conns = 0;
1408 static long
1409 find_partition (fs_server_context *context)
1411 int i = context->conns[context->i - 1].ve_ent;
1413 if (i < 0 || i >= context->ve->entry.nServers)
1414 return 0;
1415 return context->ve->entry.serverPartition[i];
1419 * Find the next fileserver for the request in `context'.
1420 * Returns a ConnCacheEntry or NULL.
1423 ConnCacheEntry *
1424 find_next_fs (fs_server_context *context,
1425 ConnCacheEntry *prev_conn,
1426 int error)
1428 if (error) {
1429 if (host_downp(error))
1430 conn_dead (prev_conn);
1431 if (volume_downp(error))
1432 volcache_mark_down (context->ve,
1433 context->conns[context->i - 1].ve_ent,
1434 error);
1435 } else if (prev_conn) {
1436 assert(prev_conn == context->conns[context->i - 1].conn);
1437 volcache_reliable_el(context->ve, context->conns[context->i - 1].ve_ent);
1440 if (context->i < context->num_conns)
1441 return context->conns[context->i++].conn;
1442 else
1443 return NULL;
1447 * Clean up a `fs_server_context'
1450 void
1451 free_fs_server_context (fs_server_context *context)
1453 int i;
1455 for (i = 0; i < context->num_conns; ++i)
1456 conn_free (context->conns[i].conn);
1458 if (context->ve)
1459 volcache_process_marks(context->ve);
1463 * Find the the file servers housing the volume for `e' and store it
1464 * in the `context'.
1468 init_fs_context (FCacheEntry *e,
1469 CredCacheEntry *ce,
1470 fs_server_context *context)
1472 VolCacheEntry *ve = e->volume;
1473 int i;
1474 int bit;
1475 int num_clones;
1476 int cell = e->fid.Cell;
1477 int ret;
1479 memset(context, 0, sizeof(*context));
1481 if (ve == NULL) {
1482 ret = volcache_getbyid (e->fid.fid.Volume, e->fid.Cell,
1483 ce, &e->volume, NULL);
1484 if (ret)
1485 return ret;
1486 ve = e->volume;
1489 ret = volume_make_uptodate (ve, ce);
1490 if (ret)
1491 return ret;
1493 bit = volcache_volid2bit (ve, e->fid.fid.Volume);
1495 if (bit == -1) {
1496 /* the volume entry is inconsistent. */
1497 volcache_invalidate_ve (ve);
1498 return ENOENT;
1501 num_clones = 0;
1502 for (i = 0; i < min(NMAXNSERVERS,ve->entry.nServers); ++i) {
1503 u_long addr = htonl(ve->entry.serverNumber[i]);
1505 if (ve->entry.serverFlags[i] & bit
1506 && addr != 0
1507 && (ve->entry.serverFlags[i] & VLSF_DONTUSE) == 0) {
1508 ConnCacheEntry *conn;
1510 conn = conn_get (cell, addr, afsport,
1511 FS_SERVICE_ID, fs_probe, ce);
1512 if (!conn_isalivep (conn))
1513 conn->rtt = INT_MAX/2 ;
1514 else if (!volcache_reliablep_el(ve, i))
1515 conn->rtt = INT_MAX/4;
1516 else
1517 conn->rtt = rx_PeerOf(conn->connection)->srtt
1518 + rand() % RTT_FUZZ - RTT_FUZZ / 2;
1519 context->conns[num_clones].conn = conn;
1520 context->conns[num_clones].ve_ent = i;
1521 ++num_clones;
1525 if (num_clones == 0)
1526 return ENOENT;
1528 context->ve = ve;
1530 qsort (context->conns, num_clones, sizeof(*context->conns),
1531 fs_rtt_cmp);
1533 context->num_conns = num_clones;
1534 context->i = 0;
1536 return 0;
1540 * Find the first file server housing the volume for `e'.
1543 ConnCacheEntry *
1544 find_first_fs (fs_server_context *context)
1546 return find_next_fs (context, NULL, 0);
1550 * Initialize the file cache in `cachedir', with these values for high
1551 * and low-water marks.
1554 void
1555 fcache_init (u_long alowvnodes,
1556 u_long ahighvnodes,
1557 int64_t alowbytes,
1558 int64_t ahighbytes,
1559 Bool recover)
1562 * Initialize all variables.
1565 #ifdef KERBEROS
1566 fhopen_working = k_hasafs ();
1567 #else
1568 fhopen_working = 0;
1569 #endif
1571 collectstats_init ();
1573 node_count = 0;
1574 lowvnodes = alowvnodes;
1575 highvnodes = ahighvnodes;
1576 lowbytes = alowbytes;
1577 highbytes = ahighbytes;
1579 hashtab = hashtabnew (FCHASHSIZE, fcachecmp, fcachehash);
1580 if (hashtab == NULL)
1581 arla_errx (1, ADEBERROR, "fcache: hashtabnew failed");
1583 lrulist = listnew ();
1584 if (lrulist == NULL)
1585 arla_errx (1, ADEBERROR, "fcache: listnew failed");
1587 invalid_heap = heap_new (ahighvnodes, expiration_time_cmp);
1588 if (invalid_heap == NULL)
1589 arla_errx (1, ADEBERROR, "fcache: heap_new failed");
1591 if (recover)
1592 fcache_recover_state ();
1594 if (LWP_CreateProcess (create_nodes, 0, 1, NULL, "fcache-create-nodes",
1595 &create_nodes_pid))
1596 arla_errx (1, ADEBERROR,
1597 "fcache: cannot create create-nodes thread");
1599 if (LWP_CreateProcess (cleaner, 0, 1, NULL, "fcache-cleaner",
1600 &cleaner_pid))
1601 arla_errx (1, ADEBERROR,
1602 "fcache: cannot create cleaner thread");
1604 if (LWP_CreateProcess (invalidator, 0, 1, NULL, "fcache-invalidator",
1605 &invalidator_pid))
1606 arla_errx (1, ADEBERROR,
1607 "fcache: cannot create invalidator thread");
1611 * set new values for those of lowvnodes, highvnodes that are not zero.
1612 * do some sanity checks
1613 * return 0 or an error code
1616 static int
1617 fcache_setvnodes(u_long alowvnodes,
1618 u_long ahighvnodes)
1620 int64_t high = highvnodes;
1621 int64_t low = lowvnodes;
1623 arla_warnx (ADEBFCACHE, "fcache_setvnodes");
1625 if (ahighvnodes != 0)
1626 high = ahighvnodes;
1628 if (alowvnodes != 0)
1629 low = alowvnodes;
1631 highvnodes = high;
1632 lowvnodes = low;
1634 if (high > highvnodes)
1635 LWP_NoYieldSignal (create_nodes);
1637 return 0;
1641 * set new values for those of lowvnodes, highvnodes that are not zero.
1642 * do some sanity checks
1643 * return 0 or an error code
1646 static int
1647 fcache_setbytes(int64_t alowbytes,
1648 int64_t ahighbytes)
1650 int64_t high = highbytes;
1651 int64_t low = lowbytes;
1653 arla_warnx (ADEBFCACHE, "fcache_setbytes");
1655 if (alowbytes != 0)
1656 low = alowbytes;
1658 if (ahighbytes != 0)
1659 high = ahighbytes;
1661 if (high < low)
1662 return EINVAL;
1664 highbytes = high;
1665 lowbytes = low;
1667 return 0;
1671 * set new high/low values for vnodes and bytes.
1672 * return 0 or an error code
1676 fcache_reinit(u_long alowvnodes,
1677 u_long ahighvnodes,
1678 int64_t alowbytes,
1679 int64_t ahighbytes)
1681 int error = fcache_setvnodes(alowvnodes, ahighvnodes);
1682 if (error)
1683 return error;
1685 return fcache_setbytes(alowbytes, ahighbytes);
1689 * Find the entry for `fid' in the hash table.
1690 * If it's found, move it to the front of `lrulist' as well.
1693 static FCacheEntry *
1694 find_entry_nolock (VenusFid fid)
1696 FCacheEntry key;
1697 FCacheEntry *e;
1699 if (hashtab == NULL)
1700 return NULL;
1702 key.fid = fid;
1703 e = (FCacheEntry *)hashtabsearch (hashtab, (void *)&key);
1704 if (e != NULL) {
1705 listdel (lrulist, e->lru_le);
1706 e->lru_le = listaddhead (lrulist, e);
1707 assert(e->lru_le);
1709 return e;
1713 * Mark `e' as having `callback' and notify the kernel.
1714 * This might be overly harsh to opened files.
1717 static void
1718 stale (FCacheEntry *e, AFSCallBack callback)
1720 if (callback.CallBackType == CBDROPPED &&
1721 e->callback.CallBackType == CBDROPPED)
1722 return;
1724 if (CheckLock (&e->lock) != 0)
1725 e->flags.sentenced = TRUE;
1726 else {
1727 ObtainWriteLock (&e->lock);
1728 fcache_poller_unref(e);
1729 e->callback = callback;
1731 if (e->flags.kernelp)
1732 break_callback (e);
1733 else
1734 e->tokens = 0;
1735 if (e->status.FileType == TYPE_DIR && e->length)
1736 throw_data(e);
1737 ReleaseWriteLock (&e->lock);
1741 struct stale_arg {
1742 VenusFid fid;
1743 AFSCallBack callback;
1747 * Iterate over all entries until we find an entry that matches in
1748 * only fid (without cell) and stale it.
1751 static Bool
1752 stale_unknown_cell (void *ptr, void *arg)
1754 FCacheEntry *e = (FCacheEntry *)ptr;
1755 struct stale_arg *sa = (struct stale_arg *)arg;
1757 if (e->fid.fid.Volume == sa->fid.fid.Volume
1758 && e->fid.fid.Vnode == sa->fid.fid.Vnode
1759 && e->fid.fid.Unique == sa->fid.fid.Unique)
1760 stale (e, sa->callback);
1762 return FALSE;
1766 * Call stale on the entry corresponding to `fid', if any.
1769 void
1770 fcache_stale_entry (VenusFid fid, AFSCallBack callback)
1772 FCacheEntry *e;
1774 if (fid.Cell == -1) {
1775 struct stale_arg arg;
1777 arg.fid = fid;
1778 arg.callback = callback;
1780 hashtabforeach (hashtab, stale_unknown_cell, &arg);
1781 return;
1784 e = find_entry_nolock (fid);
1785 if (e == NULL) {
1786 arla_warnx (ADEBFCACHE,
1787 "callback for non-existing file (%d, %u, %u, %u)",
1788 fid.Cell, fid.fid.Volume, fid.fid.Vnode, fid.fid.Unique);
1789 return;
1791 stale (e, callback);
1794 typedef struct {
1795 nnpfs_pag_t pag;
1796 int32_t cell;
1797 } fc_purgecred;
1800 * If ptr has cred arg, set it invalid
1803 static Bool
1804 purge_cred (void *ptr, void *arg)
1806 FCacheEntry *e = (FCacheEntry *)ptr;
1807 fc_purgecred *cred = (fc_purgecred *) arg;
1808 AccessEntry *ae = e->acccache;
1809 int i;
1811 if (e->fid.Cell == cred->cell || cred->cell == -1) {
1813 for(i = 0; i < NACCESS ; ++i)
1814 if(ae[i].cred == cred->pag) {
1815 ae[i].cred = ARLA_NO_AUTH_CRED;
1816 ae[i].access = ANONE;
1817 if (e->flags.kernelp)
1818 install_attr (e, FCACHE2NNPFSNODE_NO_LENGTH);
1819 break;
1822 return FALSE;
1827 * Mark cred as stale in kernel and all fcache-entries,
1828 * When cell == -1, flush all creds in this pag.
1831 void
1832 fcache_purge_cred (nnpfs_pag_t pag, int32_t cell)
1834 fc_purgecred cred;
1836 cred.pag = pag;
1837 cred.cell = cell;
1839 hashtabforeach(hashtab, purge_cred, &cred);
1843 * If ptr was retrieved from cell - volume , try to mark stale
1846 static Bool
1847 purge_volume (void *ptr, void *arg)
1849 FCacheEntry *e = (FCacheEntry *)ptr;
1850 VenusFid *fid = (VenusFid *) arg;
1851 AFSCallBack broken_callback = {0, 0, CBDROPPED};
1853 if ((e->fid.Cell == fid->Cell || fid->Cell == -1)
1854 && e->fid.fid.Volume == fid->fid.Volume) {
1855 stale (e, broken_callback);
1857 return FALSE;
1861 * Mark all entries from cell.volume as stale
1864 void
1865 fcache_purge_volume (VenusFid fid)
1867 hashtabforeach(hashtab, purge_volume, &fid);
1871 * If `ptr' was retrieved from `host', mark it as stale.
1874 static Bool
1875 purge_host (void *ptr, void *arg)
1877 FCacheEntry *e = (FCacheEntry *)ptr;
1878 u_long *host = (u_long *)arg;
1879 AFSCallBack broken_callback = {0, 0, CBDROPPED};
1881 assert (*host);
1882 if (e->host == *host)
1883 stale (e, broken_callback);
1884 return FALSE;
1888 * Mark all entries from the host `host' as stale.
1891 void
1892 fcache_purge_host (u_long host)
1894 hashtabforeach (hashtab, purge_host, &host);
1899 * If `ptr' is a mountpoint, mark it as stale.
1902 static Bool
1903 invalidate_mp (void *ptr, void *arg)
1905 FCacheEntry *e = (FCacheEntry *)ptr;
1906 AFSCallBack broken_callback = {0, 0, CBDROPPED};
1908 if (e->flags.mountp)
1909 stale (e, broken_callback);
1910 return FALSE;
1914 * Invalidate all mountpoints to force them to be reread.
1917 void
1918 fcache_invalidate_mp (void)
1920 hashtabforeach (hashtab, invalidate_mp, NULL);
1924 * Mark `entry' as not being used.
1927 void
1928 fcache_unused (FCacheEntry *entry)
1930 AssertExclLocked(&entry->lock);
1932 entry->flags.datausedp = entry->flags.attrusedp = FALSE;
1933 listdel (lrulist, entry->lru_le);
1934 entry->lru_le = listaddtail (lrulist, entry);
1935 assert (entry->lru_le);
1938 * we don't signal lrulist here since we never
1939 * free the node (usedvnode--);
1942 /* if node is deleted from kernel and from fileserver, throw the data */
1943 if (entry->flags.silly && !entry->flags.kernelp && entry->length)
1944 throw_data(entry);
1948 * make up some status that might be valid for a mount-point
1951 static void
1952 fake_mp_status (FCacheEntry *e)
1954 AFSFetchStatus *status = &e->status;
1956 status->FileType = TYPE_DIR;
1957 status->LinkCount = 100;
1958 status->UnixModeBits = 0777;
1959 status->ClientModTime = 0;
1960 status->ServerModTime = 0;
1961 status->Owner = 0;
1962 status->Group = 0;
1966 * Return true if `entry' is a mountpoint
1969 static Bool
1970 mountpointp (FCacheEntry *entry)
1972 if (entry->status.FileType == TYPE_LINK
1973 && fcache_get_status_length(&entry->status) != 0
1974 && entry->status.UnixModeBits == 0644)
1975 return TRUE;
1976 return FALSE;
1980 * Mark `entry' as mountpoint or a fake mountpoint depending on
1981 * fake_mp is used or not.
1984 void
1985 fcache_mark_as_mountpoint (FCacheEntry *entry)
1987 if (fake_mp) {
1988 entry->flags.fake_mp = TRUE;
1989 fake_mp_status (entry);
1990 } else {
1991 entry->flags.mountp = TRUE;
1996 * Update all the relevant parts of `entry' after having received new
1997 * data from the file server.
2000 static void
2001 update_entry (FCacheEntry *entry,
2002 AFSFetchStatus *status,
2003 AFSCallBack *callback,
2004 AFSVolSync *volsync,
2005 ConnCacheEntry *conn,
2006 nnpfs_pag_t cred)
2008 struct timeval tv;
2009 AccessEntry *ae;
2010 unsigned long bitmask = 0141777; /* REG, DIR, STICKY, USR, GRP, OTH */
2012 if (entry->volume && cell_issuid_by_num (entry->volume->cell))
2013 bitmask |= 0006000; /* SUID, SGID */
2015 gettimeofday (&tv, NULL);
2017 entry->status = *status;
2018 entry->status.UnixModeBits &= bitmask;
2019 if (callback) {
2020 entry->callback = *callback;
2021 entry->callback.ExpirationTime += tv.tv_sec;
2022 add_to_invalidate (entry);
2024 if (volsync) {
2025 entry->volsync = *volsync;
2026 if (entry->volume)
2027 volcache_update_volsync (entry->volume, *volsync);
2030 if (conn) {
2031 fcache_poller_reref(entry, conn);
2032 entry->host = rx_HostOf(rx_PeerOf(conn->connection));
2033 } else {
2034 fcache_poller_unref(entry);
2035 entry->host = 0;
2038 entry->anonaccess = status->AnonymousAccess;
2039 findaccess (cred, entry->acccache, &ae);
2040 ae->cred = cred;
2041 ae->access = status->CallerAccess;
2042 if (!entry->flags.mountp && mountpointp (entry))
2043 fcache_mark_as_mountpoint (entry);
2047 * Update entry, common code for do_read_attr and get_attr_bulk
2050 static void
2051 update_attr_entry (FCacheEntry *entry,
2052 AFSFetchStatus *status,
2053 AFSCallBack *callback,
2054 AFSVolSync *volsync,
2055 ConnCacheEntry *conn,
2056 nnpfs_pag_t cred)
2058 if (entry->fetched_length
2059 && entry->status.DataVersion != status->DataVersion
2060 && !entry->flags.datausedp)
2062 throw_data (entry);
2063 entry->tokens &= ~(NNPFS_DATA_R|NNPFS_DATA_W);
2066 update_entry (entry, status, callback, volsync,
2067 conn, cred);
2069 entry->tokens |= NNPFS_ATTR_R;
2070 entry->flags.attrp = TRUE;
2075 * Give up all callbacks.
2078 static int
2079 giveup_all_callbacks (uint32_t cell, uint32_t host, void *arg)
2081 CredCacheEntry *ce;
2082 ConnCacheEntry *conn;
2083 int ret;
2085 ce = cred_get (cell, 0, CRED_ANY);
2086 assert (ce != NULL);
2088 conn = conn_get (cell, host, afsport, FS_SERVICE_ID, fs_probe, ce);
2089 cred_free (ce);
2091 if (conn_isalivep (conn)) {
2093 ret = RXAFS_GiveUpAllCallBacks(conn->connection);
2094 if (ret != 0 && ret != RXGEN_OPCODE) {
2095 struct in_addr in_addr;
2097 in_addr.s_addr = rx_HostOf(rx_PeerOf(conn->connection));
2098 arla_warn (ADEBWARN, ret, "GiveUpAllCallBacks %s",
2099 inet_ntoa (in_addr));
2100 if (host_downp(ret)) {
2101 conn_dead (conn);
2102 ret = ENETDOWN;
2107 conn_free (conn);
2109 return 0;
2113 fcache_giveup_all_callbacks (void)
2115 Listitem *item;
2117 poller_foreach(giveup_all_callbacks, NULL);
2119 for (item = listtail(lrulist);
2120 item != NULL;
2121 item = listprev(lrulist, item)) {
2122 FCacheEntry *entry = (FCacheEntry *)listdata(item);
2124 if (entry->flags.attrp &&
2125 entry->flags.silly == FALSE &&
2126 entry->host != 0) {
2128 CredCacheEntry *ce;
2129 ConnCacheEntry *conn;
2130 AFSCBFids fids;
2131 AFSCBs cbs;
2132 int ret;
2134 ce = cred_get (entry->fid.Cell, 0, CRED_ANY);
2135 assert (ce != NULL);
2137 conn = conn_get (entry->fid.Cell, entry->host, afsport,
2138 FS_SERVICE_ID, fs_probe, ce);
2139 cred_free (ce);
2141 fids.len = cbs.len = 1;
2142 fids.val = &entry->fid.fid;
2143 cbs.val = &entry->callback;
2145 if (conn_isalivep (conn)) {
2146 ret = RXAFS_GiveUpCallBacks (conn->connection, &fids, &cbs);
2147 if (ret) {
2148 struct in_addr in_addr;
2150 in_addr.s_addr = rx_HostOf(rx_PeerOf(conn->connection));
2151 arla_warn (ADEBFCACHE, ret, "RXAFS_GiveUpCallBacks %s",
2152 inet_ntoa (in_addr));
2155 conn_free (conn);
2158 return 0; /* XXX */
2162 * discard all cached attrs to force revalidation of entries
2163 * intended for reconnect after disconnected mode.
2166 void
2167 fcache_discard_attrs(void)
2169 Listitem *item;
2171 for (item = listtail(lrulist);
2172 item != NULL;
2173 item = listprev(lrulist, item)) {
2174 FCacheEntry *entry = (FCacheEntry *)listdata(item);
2176 if (entry->flags.attrp &&
2177 entry->flags.silly == FALSE)
2178 entry->flags.attrp = FALSE;
2183 * Obtain new callbacks for all entries in the cache.
2187 fcache_reobtain_callbacks (struct nnpfs_cred *cred)
2189 Listitem *item;
2190 int ret;
2192 for (item = listtail(lrulist);
2193 item != NULL;
2194 item = listprev(lrulist, item)) {
2195 FCacheEntry *entry = (FCacheEntry *)listdata(item);
2197 ObtainWriteLock (&entry->lock);
2198 if (entry->flags.usedp &&
2199 entry->flags.silly == FALSE &&
2200 entry->host != 0) {
2202 CredCacheEntry *ce;
2203 ConnCacheEntry *conn;
2204 AFSFetchStatus status;
2205 AFSCallBack callback;
2206 AFSVolSync volsync;
2207 VolCacheEntry *vol;
2209 ce = cred_get (entry->fid.Cell, cred->pag, CRED_ANY);
2210 assert (ce != NULL);
2212 conn = conn_get (entry->fid.Cell, entry->host, afsport,
2213 FS_SERVICE_ID, fs_probe, ce);
2214 if (!conn_isalivep(conn))
2215 goto out;
2217 * does this belong here?
2220 ret = volcache_getbyid (entry->fid.fid.Volume,
2221 entry->fid.Cell, ce, &vol, NULL);
2222 if (ret == 0)
2223 entry->volume = vol;
2225 ret = RXAFS_FetchStatus (conn->connection,
2226 &entry->fid.fid,
2227 &status,
2228 &callback,
2229 &volsync);
2230 if (ret)
2231 arla_warn (ADEBFCACHE, ret, "RXAFS_FetchStatus");
2232 else {
2233 update_attr_entry (entry, &status, &callback, &volsync,
2234 conn, ce->cred);
2235 if (entry->flags.kernelp)
2236 break_callback (entry);
2238 fcache_counter.fetch_attr++;
2239 out:
2240 if (conn)
2241 conn_free (conn);
2242 cred_free (ce);
2244 ReleaseWriteLock (&entry->lock);
2246 return 0; /* XXX */
2250 * Return true iff there's any point in trying the next fs.
2253 static Bool
2254 try_next_fs (int error, const VenusFid *fid)
2256 switch (error) {
2257 #ifdef KERBEROS
2258 case RXKADUNKNOWNKEY:
2259 #endif
2260 case ARLA_CALL_DEAD :
2261 case ARLA_INVALID_OPERATION :
2262 case ARLA_CALL_TIMEOUT :
2263 case ARLA_EOF :
2264 case ARLA_PROTOCOL_ERROR :
2265 case ARLA_USER_ABORT :
2266 case ARLA_ADDRINUSE :
2267 case ARLA_MSGSIZE :
2268 case ARLA_VSALVAGE :
2269 case ARLA_VNOSERVICE :
2270 case ARLA_VOFFLINE :
2271 case ARLA_VBUSY :
2272 case ARLA_VIO :
2273 return TRUE;
2274 case ARLA_VNOVOL :
2275 case ARLA_VMOVED :
2276 if (fid && !volcache_reliablep (fid->fid.Volume, fid->Cell))
2277 volcache_invalidate (fid->fid.Volume, fid->Cell);
2278 return TRUE;
2279 case 0 :
2280 return FALSE;
2281 default :
2282 return FALSE;
2287 * If the whole file is fetched as we last saw it, lets write down
2288 * the whole file to the fileserver. If the file is shrinking,
2289 * make sure we don't cache non-existing bytes.
2292 static uint64_t
2293 new_fetched_length(FCacheEntry *entry, uint64_t cache_file_size)
2295 uint64_t have_len;
2297 AssertExclLocked(&entry->lock);
2299 if (entry->fetched_length == fcache_get_status_length(&entry->status))
2300 have_len = cache_file_size;
2301 else {
2302 have_len = entry->fetched_length;
2303 /* have file shrinked ? */
2304 if (have_len > cache_file_size)
2305 have_len = cache_file_size;
2308 return have_len;
2312 * Fetch the attributes for the file in `entry' from the file_server,
2313 * using the credentials in `ce' and returning the connection in
2314 * `ret_conn'
2316 * `entry' must be write-locked.
2318 * If an error code is returned `fs_server_context' is already freed.
2319 * If everything is ok, `fs_server_context' must be freed by the caller.
2322 static int
2323 do_read_attr (FCacheEntry *entry,
2324 CredCacheEntry *ce,
2325 ConnCacheEntry **ret_conn,
2326 fs_server_context *ret_context)
2328 ConnCacheEntry *conn;
2329 AFSFetchStatus status;
2330 AFSCallBack callback;
2331 AFSVolSync volsync;
2332 struct collect_stat collectstat;
2333 int ret;
2335 AssertExclLocked(&entry->lock);
2337 *ret_conn = NULL;
2339 ret = init_fs_context(entry, ce, ret_context);
2340 if (ret)
2341 return ret;
2343 for (conn = find_first_fs (ret_context);
2344 conn != NULL;
2345 conn = find_next_fs (ret_context, conn, ret)) {
2347 collectstats_start(&collectstat);
2348 ret = RXAFS_FetchStatus (conn->connection,
2349 &entry->fid.fid,
2350 &status,
2351 &callback,
2352 &volsync);
2353 collectstats_stop(&collectstat, entry, conn,
2354 find_partition(ret_context),
2355 arla_STATISTICS_REQTYPE_FETCHSTATUS, 1);
2356 arla_warnx (ADEBFCACHE, "trying to fetch status: %d", ret);
2357 if (!try_next_fs (ret, &entry->fid))
2358 break;
2360 if (ret) {
2361 arla_warn (ADEBFCACHE, ret, "fetch-status");
2362 if (host_downp(ret))
2363 ret = ENETDOWN;
2364 free_fs_server_context (ret_context);
2365 return ret;
2368 fcache_counter.fetch_attr++;
2370 update_attr_entry (entry, &status, &callback, &volsync,
2371 conn, ce->cred);
2373 AssertExclLocked(&entry->lock);
2375 *ret_conn = conn;
2376 return 0;
2381 * Read the attributes of `entry' from the file server and store them.
2382 * `e' must be write-locked.
2386 read_attr (FCacheEntry *entry, CredCacheEntry *ce)
2388 int ret;
2389 ConnCacheEntry *conn;
2390 fs_server_context context;
2392 AssertExclLocked(&entry->lock);
2394 init_fs_server_context (&context);
2395 ret = do_read_attr (entry, ce, &conn, &context);
2396 if (ret)
2397 return ret;
2398 free_fs_server_context (&context);
2399 return 0;
2403 * Read the contents of `entry' from the file server and store it.
2406 static int
2407 read_data (FCacheEntry *entry, ConnCacheEntry *conn, CredCacheEntry *ce,
2408 long partition)
2410 struct rx_call *call;
2411 int ret = 0;
2412 uint64_t wanted_length, nbytes = 0;
2413 uint64_t sizefs;
2414 int64_t sizediff;
2415 int fd;
2416 AFSFetchStatus status;
2417 AFSCallBack callback;
2418 AFSVolSync volsync;
2419 struct collect_stat collectstat;
2420 uint32_t sizefs4;
2422 arla_warnx (ADEBMISC, "read_data");
2424 AssertExclLocked(&entry->lock);
2426 if (connected_mode == DISCONNECTED) {
2427 ret = ENETDOWN;
2428 goto out;
2431 /* are we already done ? */
2432 if (entry->wanted_length <= entry->fetched_length) {
2433 ret = 0;
2434 goto out;
2437 /* figure out how much more then we need we want to fetch */
2438 wanted_length = stats_fetch_round(conn, partition, entry->wanted_length);
2439 if (wanted_length > fcache_get_status_length(&entry->status))
2440 wanted_length = fcache_get_status_length(&entry->status);
2442 /* we need more space ? */
2443 if (wanted_length > entry->length)
2444 nbytes = wanted_length - entry->length;
2446 if (usedbytes + nbytes > highbytes) {
2447 ret = fcache_need_bytes (nbytes);
2448 if (ret)
2449 goto out;
2452 if (usedbytes + nbytes > highbytes) {
2453 arla_warnx (ADEBWARN, "Out of space, not enough cache "
2454 "(file-length: %lu need bytes: %ld usedbytes: %ld)",
2455 (unsigned long)fcache_get_status_length(&entry->status),
2456 (long)nbytes, (long)usedbytes);
2457 ret = ENOSPC;
2458 goto out;
2461 again:
2462 /* now go talk to the world */
2463 call = rx_NewCall (conn->connection);
2464 if (call == NULL) {
2465 arla_warnx (ADEBMISC, "rx_NewCall failed");
2466 ret = ENOMEM;
2467 goto out;
2470 collectstats_start(&collectstat);
2471 if (conn_get_fs_support64(conn)) {
2472 ret = StartRXAFS_FetchData64 (call,
2473 &entry->fid.fid,
2474 entry->fetched_length,
2475 wanted_length - entry->fetched_length);
2476 if (ret == RXGEN_OPCODE) {
2477 rx_EndCall(call,ret);
2478 conn_set_fs_support64(conn, FALSE);
2479 goto again;
2481 } else if (entry->fetched_length >> 32)
2482 ret = EFBIG;
2483 else
2484 ret = StartRXAFS_FetchData (call,
2485 &entry->fid.fid,
2486 entry->fetched_length,
2487 wanted_length - entry->fetched_length);
2489 if(ret) {
2490 arla_warn (ADEBFCACHE, ret, "fetch-data");
2491 rx_EndCall(call,ret);
2492 goto out;
2495 if (conn_get_fs_support64(conn)) {
2496 ret = rx_Read (call, &sizefs4, sizeof(sizefs4));
2497 if (ret != sizeof(sizefs4)) {
2498 ret = rx_GetCallError(call);
2499 if (ret == RXGEN_OPCODE && conn_get_fs_support64(conn)) {
2500 rx_EndCall(call,0);
2501 conn_set_fs_support64(conn, FALSE);
2502 goto again;
2504 ret = conv_to_arla_errno(ret);
2505 arla_warn (ADEBFCACHE, ret, "Error reading length");
2506 rx_EndCall(call, 0);
2507 goto out;
2509 sizefs = (uint64_t)ntohl(sizefs4) << 32;
2510 } else
2511 sizefs = 0;
2512 ret = rx_Read (call, &sizefs4, sizeof(sizefs4));
2513 if (ret != sizeof(sizefs4)) {
2514 ret = conv_to_arla_errno(rx_GetCallError(call));
2515 arla_warn (ADEBFCACHE, ret, "Error reading length");
2516 rx_EndCall(call, 0);
2517 goto out;
2519 sizefs |= ntohl (sizefs4);
2520 if (sizefs < 0)
2521 sizefs = 0;
2523 fd = fcache_open_file (entry, O_RDWR);
2524 if (fd < 0) {
2525 ret = errno;
2526 arla_warn (ADEBFCACHE, ret, "open cache file %u",
2527 (unsigned)entry->index);
2528 rx_EndCall(call, 0);
2529 goto out;
2532 if (ftruncate(fd, fcache_get_status_length(&entry->status)) < 0) {
2533 close(fd);
2534 ret = errno;
2535 rx_EndCall(call, 0);
2536 goto out;
2539 ret = copyrx2fd (call, fd, entry->fetched_length, sizefs);
2540 close (fd);
2541 if (ret) {
2542 arla_warn (ADEBFCACHE, ret, "copyrx2fd");
2543 rx_EndCall(call, ret);
2544 goto out;
2547 if (conn_get_fs_support64(conn)) {
2548 ret = EndRXAFS_FetchData64 (call,
2549 &status,
2550 &callback,
2551 &volsync);
2552 } else {
2553 ret = EndRXAFS_FetchData (call,
2554 &status,
2555 &callback,
2556 &volsync);
2558 if (ret)
2559 arla_warn (ADEBWARN, ret, "EndRXAFS_FetchData");
2560 ret = rx_EndCall (call, ret);
2561 if(ret) {
2562 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
2563 goto out;
2565 collectstats_stop(&collectstat, entry, conn,
2566 partition, arla_STATISTICS_REQTYPE_FETCHDATA, sizefs);
2568 entry->fetched_length += sizefs;
2569 sizediff = entry->fetched_length - entry->length;
2570 entry->length = entry->fetched_length;
2571 usedbytes += sizediff;
2573 fcache_counter.fetch_data++;
2575 update_entry (entry, &status, &callback, &volsync,
2576 conn, ce->cred);
2578 entry->tokens |= NNPFS_DATA_R;
2580 out:
2581 AssertExclLocked(&entry->lock);
2583 return ret;
2587 * Write the contents of the cache file back to the file server.
2589 * If data_entry is not null, use that as data source.
2593 write_data (FCacheEntry *entry, FCacheEntry *data_entry,
2594 AFSStoreStatus *storestatus, CredCacheEntry *ce)
2596 FCacheEntry *fd_entry = entry;
2597 ConnCacheEntry *conn;
2598 struct rx_call *call;
2599 int ret;
2600 uint64_t sizefs;
2601 uint64_t have_len;
2602 uint64_t status_len;
2603 int fd;
2604 struct stat statinfo;
2605 AFSFetchStatus status;
2606 AFSVolSync volsync;
2607 fs_server_context context;
2608 struct collect_stat collectstat;
2610 AssertExclLocked(&entry->lock);
2612 if (data_entry) {
2613 AssertExclLocked(&data_entry->lock);
2614 fd_entry = data_entry;
2616 fd = fcache_open_file (fd_entry, O_RDONLY);
2617 if (fd < 0) {
2618 ret = errno;
2619 arla_warn (ADEBFCACHE, ret, "open cache file %u",
2620 (unsigned)fd_entry->index);
2621 return ret;
2624 if (fstat (fd, &statinfo) < 0) {
2625 ret = errno;
2626 close (fd);
2627 arla_warn (ADEBFCACHE, ret, "stat cache file %u",
2628 (unsigned)fd_entry->index);
2629 return ret;
2631 sizefs = statinfo.st_size;
2633 have_len = new_fetched_length(fd_entry, sizefs);
2634 if (!data_entry) {
2635 if (fcache_get_status_length(&entry->status) < have_len)
2636 fcache_set_status_length(&entry->status, have_len);
2637 fcache_update_length (entry, have_len, have_len);
2639 status_len = fcache_get_status_length(&fd_entry->status);
2641 if (connected_mode != CONNECTED || entry->flags.silly) {
2642 close (fd);
2643 return 0;
2646 ret = init_fs_context(entry, ce, &context);
2647 if (ret)
2648 return ret;
2650 for (conn = find_first_fs (&context);
2651 conn != NULL;
2652 conn = find_next_fs (&context, conn, ret)) {
2654 again:
2655 call = rx_NewCall (conn->connection);
2656 if (call == NULL) {
2657 arla_warnx (ADEBMISC, "rx_NewCall failed");
2658 ret = ENOMEM;
2659 break;
2662 collectstats_start(&collectstat);
2663 if (conn_get_fs_support64(conn)) {
2664 ret = StartRXAFS_StoreData64 (call, &entry->fid.fid,
2665 storestatus,
2667 have_len,
2668 status_len);
2669 if (ret == RXGEN_OPCODE) {
2670 rx_EndCall(call,ret);
2671 conn_set_fs_support64(conn, FALSE);
2672 goto again;
2674 } else if ((uint64_t)have_len >> 32) {
2675 ret = EFBIG;
2676 } else {
2677 ret = StartRXAFS_StoreData (call, &entry->fid.fid,
2678 storestatus,
2680 have_len,
2681 status_len);
2683 if (host_downp(ret)) {
2684 rx_EndCall(call, ret);
2685 continue;
2686 } else if (ret) {
2687 arla_warn (ADEBFCACHE, ret, "store-data");
2688 rx_EndCall(call, 0);
2689 break;
2692 ret = copyfd2rx (fd, call, 0, have_len);
2693 if (ret == RXGEN_OPCODE && conn_get_fs_support64(conn)) {
2694 rx_EndCall(call,ret);
2695 conn_set_fs_support64(conn, FALSE);
2696 goto again;
2697 } else if (ret) {
2698 rx_EndCall(call, ret);
2699 arla_warn (ADEBFCACHE, ret, "copyfd2rx");
2700 break;
2703 if (conn_get_fs_support64(conn)) {
2704 ret = EndRXAFS_StoreData64 (call,
2705 &status,
2706 &volsync);
2707 if (ret == RXGEN_OPCODE) {
2708 rx_EndCall(call, 0);
2709 conn_set_fs_support64(conn, FALSE);
2710 goto again;
2712 } else {
2713 ret = EndRXAFS_StoreData (call,
2714 &status,
2715 &volsync);
2717 if (ret) {
2718 rx_EndCall (call, ret);
2719 arla_warnx (ADEBFCACHE, "EndRXAFS_StoreData");
2720 break;
2723 ret = rx_EndCall (call, 0);
2724 if (ret) {
2725 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
2727 collectstats_stop(&collectstat, entry, conn,
2728 find_partition(&context),
2729 arla_STATISTICS_REQTYPE_STOREDATA, sizefs);
2730 break;
2733 if (conn != NULL) {
2734 if (ret == 0) {
2735 fcache_counter.store_data++;
2736 update_entry (entry, &status, NULL, &volsync,
2737 conn, ce->cred);
2738 } else {
2739 #if 0
2741 * We can't do this, it will corrupt the cache since nnpfs
2742 * will still think it have the data, and then when we
2743 * write back the file to the fileserver, it will be
2744 * filled with zeros. Happens if you are unlucky so store
2745 * a file at the same moment as your credentials expire.
2747 ftruncate (fd, 0);
2748 usedbytes -= entry->length;
2749 entry->length = 0;
2750 entry->wanted_length = 0;
2751 entry->fetched_length = 0;
2752 #endif
2755 if (host_downp(ret))
2756 ret = ENETDOWN;
2757 free_fs_server_context (&context);
2758 AssertExclLocked(&entry->lock);
2759 if (data_entry)
2760 AssertExclLocked(&data_entry->lock);
2761 close (fd);
2762 return ret;
2766 * Truncate the file in `entry' to `size' bytes.
2770 truncate_file (FCacheEntry *entry, uint64_t size,
2771 AFSStoreStatus *storestatus, CredCacheEntry *ce)
2773 fs_server_context context;
2774 ConnCacheEntry *conn;
2775 struct rx_call *call;
2776 AFSFetchStatus status;
2777 AFSVolSync volsync;
2778 uint64_t have_len;
2779 int ret;
2781 AssertExclLocked(&entry->lock);
2783 if (connected_mode != CONNECTED)
2784 return 0;
2786 have_len = new_fetched_length(entry, size);
2788 ret = init_fs_context(entry, ce, &context);
2789 if (ret)
2790 return ret;
2792 for (conn = find_first_fs (&context);
2793 conn != NULL;
2794 conn = find_next_fs (&context, conn, ret)) {
2796 again:
2797 call = rx_NewCall (conn->connection);
2798 if (call == NULL) {
2799 arla_warnx (ADEBMISC, "rx_NewCall failed");
2800 ret = ENOMEM;
2801 break;
2804 if (conn_get_fs_support64(conn)) {
2805 ret = StartRXAFS_StoreData64 (call,
2806 &entry->fid.fid,
2807 storestatus,
2808 size,
2810 size);
2811 if (ret == RXGEN_OPCODE) {
2812 rx_EndCall(call, ret);
2813 conn_set_fs_support64(conn, FALSE);
2814 goto again;
2816 } else if (size >> 32)
2817 ret = EFBIG;
2818 else
2819 ret = StartRXAFS_StoreData (call,
2820 &entry->fid.fid,
2821 storestatus,
2822 size,
2824 size);
2825 if (host_downp(ret)) {
2826 rx_EndCall(call, ret);
2827 continue;
2828 } else if(ret) {
2829 arla_warn (ADEBFCACHE, ret, "store-data");
2830 rx_EndCall(call, 0);
2831 break;
2834 if (conn_get_fs_support64(conn)) {
2835 ret = EndRXAFS_StoreData64 (call,
2836 &status,
2837 &volsync);
2838 if (ret == RXGEN_OPCODE) {
2839 rx_EndCall(call, 0);
2840 conn_set_fs_support64(conn, FALSE);
2841 goto again;
2843 } else {
2844 ret = EndRXAFS_StoreData (call,
2845 &status,
2846 &volsync);
2848 if (ret) {
2849 rx_EndCall (call, ret);
2850 arla_warnx (ADEBFCACHE, "EndRXAFS_StoreData");
2851 break;
2854 ret = rx_EndCall (call, 0);
2855 if (ret)
2856 arla_warn (ADEBFCACHE, ret, "rx_EndCall");
2858 break;
2861 if (ret == 0) {
2862 int fd;
2864 fd = fcache_open_file (entry, O_RDWR);
2865 if (fd < 0) {
2866 ret = errno;
2867 arla_warn (ADEBFCACHE, ret, "open fache file %u",
2868 (unsigned)entry->index);
2869 free_fs_server_context(&context);
2870 return ret;
2873 if(ftruncate (fd, size) < 0) {
2874 ret = errno;
2875 arla_warn (ADEBFCACHE, ret, "ftruncate %ld", (long)size);
2876 close (fd);
2877 free_fs_server_context(&context);
2878 return ret;
2881 close (fd);
2883 fcache_update_length (entry, size, have_len);
2885 fcache_counter.store_data++;
2886 update_entry (entry, &status, NULL, &volsync,
2887 conn, ce->cred);
2890 free_fs_server_context (&context);
2892 if (host_downp(ret))
2893 ret = ENETDOWN;
2895 AssertExclLocked(&entry->lock);
2896 return ret;
2900 * Set the attributes of the file in `entry' to `status'.
2904 write_attr (FCacheEntry *entry,
2905 const AFSStoreStatus *store_status,
2906 CredCacheEntry *ce)
2908 ConnCacheEntry *conn = NULL;
2909 int ret;
2910 AFSFetchStatus status;
2911 AFSVolSync volsync;
2913 AssertExclLocked(&entry->lock);
2915 /* Don't write attributes to deleted files */
2916 if (entry->flags.silly)
2917 return 0;
2919 if (connected_mode == CONNECTED) {
2920 fs_server_context context;
2921 struct collect_stat collectstat;
2923 ret = init_fs_context(entry, ce, &context);
2924 if (ret)
2925 return ret;
2927 for (conn = find_first_fs (&context);
2928 conn != NULL;
2929 conn = find_next_fs (&context, conn, ret)) {
2931 collectstats_start(&collectstat);
2932 ret = RXAFS_StoreStatus (conn->connection,
2933 &entry->fid.fid,
2934 store_status,
2935 &status,
2936 &volsync);
2937 if (host_downp(ret)) {
2938 continue;
2939 } else if (ret) {
2940 arla_warn (ADEBFCACHE, ret, "store-status");
2941 free_fs_server_context (&context);
2942 conn = NULL;
2943 goto out;
2945 conn_ref(conn);
2946 break;
2949 if (ret == 0)
2950 collectstats_stop(&collectstat, entry, conn,
2951 find_partition(&context),
2952 arla_STATISTICS_REQTYPE_STORESTATUS, 1);
2955 free_fs_server_context (&context);
2957 if (host_downp(ret)) {
2958 ret = ENETDOWN;
2959 goto out;
2961 update_entry (entry, &status, NULL, &volsync, conn, ce->cred);
2963 } else {
2964 assert (conn == NULL);
2966 fcache_counter.store_attr++;
2967 if (store_status->Mask & SS_MODTIME) {
2968 entry->status.ClientModTime = store_status->ClientModTime;
2969 entry->status.ServerModTime = store_status->ClientModTime;
2971 if (store_status->Mask & SS_OWNER)
2972 entry->status.Owner = store_status->Owner;
2973 if (store_status->Mask & SS_GROUP)
2974 entry->status.Group = store_status->Group;
2975 if (store_status->Mask & SS_MODEBITS)
2976 entry->status.UnixModeBits = store_status->UnixModeBits;
2977 ret = 0;
2980 out:
2981 if (conn)
2982 conn_free(conn);
2983 AssertExclLocked(&entry->lock);
2985 return ret;
2989 * Create a file.
2993 create_file (FCacheEntry *dir_entry,
2994 const char *name, AFSStoreStatus *store_attr,
2995 VenusFid *child_fid, AFSFetchStatus *fetch_attr,
2996 CredCacheEntry *ce)
2998 ConnCacheEntry *conn = NULL;
2999 int ret;
3000 AFSFid OutFid;
3001 FCacheEntry *child_entry;
3002 AFSFetchStatus status;
3003 AFSCallBack callback;
3004 AFSVolSync volsync;
3005 int fd;
3007 AssertExclLocked(&dir_entry->lock);
3009 if (connected_mode == CONNECTED) {
3010 fs_server_context context;
3012 ret = init_fs_context(dir_entry, ce, &context);
3013 if (ret)
3014 return ret;
3016 for (conn = find_first_fs (&context);
3017 conn != NULL;
3018 conn = find_next_fs (&context, conn, ret)) {
3020 ret = RXAFS_CreateFile (conn->connection,
3021 &dir_entry->fid.fid,
3022 name,
3023 store_attr,
3024 &OutFid,
3025 fetch_attr,
3026 &status,
3027 &callback,
3028 &volsync);
3029 if (host_downp(ret)) {
3030 continue;
3031 } else if (ret) {
3032 free_fs_server_context (&context);
3033 arla_warn (ADEBFCACHE, ret, "CreateFile");
3034 conn = NULL;
3035 goto out;
3037 conn_ref(conn);
3038 break;
3041 free_fs_server_context (&context);
3043 if (host_downp(ret)) {
3044 ret = ENETDOWN;
3045 goto out;
3048 fetch_attr->CallerAccess |= AADMIN;
3050 update_entry (dir_entry, &status, &callback, &volsync,
3051 conn, ce->cred);
3053 } else {
3054 static int fakefid = 1001;
3056 assert(conn == NULL);
3058 ret = 0;
3060 OutFid.Volume = dir_entry->fid.fid.Volume;
3061 OutFid.Vnode = fakefid;
3062 OutFid.Unique = fakefid;
3063 fakefid += 2;
3065 fetch_attr->InterfaceVersion = 1;
3066 fetch_attr->FileType = TYPE_FILE;
3067 fetch_attr->LinkCount = 1;
3068 fetch_attr->Length = 0;
3069 fetch_attr->DataVersion = 1;
3070 fetch_attr->Author = store_attr->Owner;
3071 fetch_attr->Owner = store_attr->Owner;
3072 fetch_attr->CallerAccess = dir_entry->status.CallerAccess;
3073 fetch_attr->AnonymousAccess = dir_entry->status.AnonymousAccess;
3074 fetch_attr->UnixModeBits = store_attr->UnixModeBits;
3075 fetch_attr->ParentVnode = dir_entry->fid.fid.Vnode;
3076 fetch_attr->ParentUnique = dir_entry->fid.fid.Unique;
3077 fetch_attr->ResidencyMask = 1;
3078 fetch_attr->ClientModTime = store_attr->ClientModTime;
3079 fetch_attr->ServerModTime = store_attr->ClientModTime;
3080 fetch_attr->Group = store_attr->Group;
3081 fetch_attr->SyncCounter = 0;
3082 fetch_attr->DataVersionHigh = 0;
3083 fetch_attr->LockCount = 0;
3084 fetch_attr->LengthHigh = 0;
3085 fetch_attr->ErrorCode = 0;
3088 child_fid->Cell = dir_entry->fid.Cell;
3089 child_fid->fid = OutFid;
3091 ret = fcache_get (&child_entry, *child_fid, ce);
3092 if (ret) {
3093 arla_warn (ADEBFCACHE, ret, "fcache_get");
3094 goto out;
3097 update_entry (child_entry, fetch_attr, NULL, NULL,
3098 conn, ce->cred);
3100 child_entry->flags.attrp = TRUE;
3102 fd = fcache_open_file (child_entry, O_WRONLY);
3103 if (fd < 0) {
3104 ret = errno;
3105 arla_warn (ADEBFCACHE, ret, "open cache file %u",
3106 (unsigned)child_entry->index);
3107 fcache_release(child_entry);
3108 goto out;
3110 if (ftruncate (fd, 0) < 0) {
3111 ret = errno;
3112 arla_warn (ADEBFCACHE, ret, "ftruncate cache file %u",
3113 (unsigned)child_entry->index);
3114 close (fd);
3115 fcache_release(child_entry);
3116 goto out;
3118 close (fd);
3119 child_entry->length = 0;
3121 child_entry->tokens |= NNPFS_ATTR_R | NNPFS_DATA_R | NNPFS_DATA_W;
3123 fcache_release(child_entry);
3125 out:
3126 if (conn)
3127 conn_free(conn);
3129 AssertExclLocked(&dir_entry->lock);
3131 return ret;
3135 * Create a directory.
3139 create_directory (FCacheEntry *dir_entry,
3140 const char *name, AFSStoreStatus *store_attr,
3141 VenusFid *child_fid, AFSFetchStatus *fetch_attr,
3142 CredCacheEntry *ce)
3144 ConnCacheEntry *conn = NULL;
3145 int ret;
3146 AFSFid OutFid;
3147 FCacheEntry *child_entry;
3148 AFSFetchStatus status;
3149 AFSCallBack callback;
3150 AFSVolSync volsync;
3153 AssertExclLocked(&dir_entry->lock);
3155 if (connected_mode == CONNECTED) {
3156 fs_server_context context;
3158 ret = init_fs_context(dir_entry, ce, &context);
3159 if (ret)
3160 return ret;
3162 for (conn = find_first_fs (&context);
3163 conn != NULL;
3164 conn = find_next_fs (&context, conn, ret)) {
3166 ret = RXAFS_MakeDir (conn->connection,
3167 &dir_entry->fid.fid,
3168 name,
3169 store_attr,
3170 &OutFid,
3171 fetch_attr,
3172 &status,
3173 &callback,
3174 &volsync);
3176 if (host_downp(ret)) {
3177 continue;
3178 } else if (ret) {
3179 free_fs_server_context (&context);
3180 arla_warn (ADEBFCACHE, ret, "MakeDir");
3181 conn = NULL;
3182 goto out;
3184 conn_ref(conn);
3185 break;
3187 free_fs_server_context (&context);
3189 if (host_downp(ret)) {
3190 ret = ENETDOWN;
3191 goto out;
3194 update_entry (dir_entry, &status, &callback, &volsync,
3195 conn, ce->cred);
3196 } else {
3197 static int fakedir = 1000;
3199 ret = 0;
3201 assert(conn == NULL);
3203 OutFid.Volume = dir_entry->fid.fid.Volume;
3204 OutFid.Vnode = fakedir;
3205 OutFid.Unique = fakedir;
3206 fakedir += 2;
3208 fetch_attr->InterfaceVersion = 1;
3209 fetch_attr->FileType = TYPE_DIR;
3210 fetch_attr->LinkCount = 2;
3211 fetch_attr->Length = AFSDIR_PAGESIZE;
3212 fetch_attr->DataVersion = 1;
3213 fetch_attr->Author = store_attr->Owner;
3214 fetch_attr->Owner = store_attr->Owner;
3215 fetch_attr->CallerAccess = dir_entry->status.CallerAccess;
3216 fetch_attr->AnonymousAccess = dir_entry->status.AnonymousAccess;
3217 fetch_attr->UnixModeBits = store_attr->UnixModeBits;
3218 fetch_attr->ParentVnode = dir_entry->fid.fid.Vnode;
3219 fetch_attr->ParentUnique = dir_entry->fid.fid.Unique;
3220 fetch_attr->ResidencyMask = 1;
3221 fetch_attr->ClientModTime = store_attr->ClientModTime;
3222 fetch_attr->ServerModTime = store_attr->ClientModTime;
3223 fetch_attr->Group = store_attr->Group;
3224 fetch_attr->SyncCounter = 0;
3225 fetch_attr->DataVersionHigh = 0;
3226 fetch_attr->LockCount = 0;
3227 fetch_attr->LengthHigh = 0;
3228 fetch_attr->ErrorCode = 0;
3231 child_fid->Cell = dir_entry->fid.Cell;
3232 child_fid->fid = OutFid;
3234 ret = fcache_get (&child_entry, *child_fid, ce);
3235 if (ret) {
3236 arla_warn (ADEBFCACHE, ret, "fcache_get");
3237 goto out;
3240 assert(child_entry->length == 0);
3242 update_entry (child_entry, fetch_attr, NULL, NULL,
3243 conn, ce->cred);
3245 child_entry->flags.attrp = TRUE;
3247 ret = adir_mkdir (child_entry, child_fid->fid, dir_entry->fid.fid);
3248 if (ret) {
3249 arla_warn (ADEBFCACHE, ret, "adir_mkdir");
3250 fcache_release(child_entry);
3251 goto out;
3254 child_entry->tokens |= NNPFS_ATTR_R | NNPFS_DATA_R | NNPFS_DATA_W;
3256 fcache_release(child_entry);
3258 out:
3259 if (conn)
3260 conn_free(conn);
3261 AssertExclLocked(&dir_entry->lock);
3262 return ret;
3266 * Create a symbolic link.
3268 * Note: create_symlink->flags.kernelp is not set on success
3269 * and that must be done by the caller.
3273 create_symlink (FCacheEntry *dir_entry,
3274 const char *name, AFSStoreStatus *store_attr,
3275 VenusFid *child_fid, AFSFetchStatus *fetch_attr,
3276 const char *contents,
3277 CredCacheEntry *ce)
3279 int ret;
3280 ConnCacheEntry *conn;
3281 AFSFid OutFid;
3282 FCacheEntry *child_entry;
3283 AFSVolSync volsync;
3284 AFSFetchStatus new_status;
3285 fs_server_context context;
3287 AssertExclLocked(&dir_entry->lock);
3289 if (connected_mode != CONNECTED)
3290 return EINVAL;
3292 ret = init_fs_context(dir_entry, ce, &context);
3293 if (ret)
3294 return ret;
3296 for (conn = find_first_fs (&context);
3297 conn != NULL;
3298 conn = find_next_fs (&context, conn, ret)) {
3300 ret = RXAFS_Symlink (conn->connection,
3301 &dir_entry->fid.fid,
3302 name,
3303 contents,
3304 store_attr,
3305 &OutFid,
3306 fetch_attr,
3307 &new_status,
3308 &volsync);
3309 if (host_downp(ret)) {
3310 continue;
3311 } else if (ret) {
3312 arla_warn (ADEBFCACHE, ret, "Symlink");
3313 free_fs_server_context (&context);
3314 conn = NULL;
3315 goto out;
3317 conn_ref(conn);
3318 break;
3320 free_fs_server_context (&context);
3322 if (host_downp(ret)) {
3323 ret = ENETDOWN;
3324 goto out;
3327 update_entry (dir_entry, &new_status, NULL, &volsync,
3328 conn, ce->cred);
3330 child_fid->Cell = dir_entry->fid.Cell;
3331 child_fid->fid = OutFid;
3333 ret = fcache_get (&child_entry, *child_fid, ce);
3334 if (ret) {
3335 arla_warn (ADEBFCACHE, ret, "fcache_get");
3336 goto out;
3339 update_entry (child_entry, fetch_attr, NULL, NULL,
3340 conn, ce->cred);
3343 * flags.kernelp is set in cm_symlink since the symlink
3344 * might be a mountpoint and this entry is never install
3345 * into the kernel.
3348 child_entry->flags.attrp = TRUE;
3349 child_entry->tokens |= NNPFS_ATTR_R;
3351 fcache_release(child_entry);
3353 out:
3354 if (conn)
3355 conn_free(conn);
3356 AssertExclLocked(&dir_entry->lock);
3357 return ret;
3361 * Create a hard link.
3365 create_link (FCacheEntry *dir_entry,
3366 const char *name,
3367 FCacheEntry *existing_entry,
3368 CredCacheEntry *ce)
3370 ConnCacheEntry *conn = NULL;
3371 int ret;
3372 AFSFetchStatus new_status;
3373 AFSFetchStatus status;
3374 AFSVolSync volsync;
3375 fs_server_context context;
3377 AssertExclLocked(&dir_entry->lock);
3379 if (connected_mode != CONNECTED)
3380 return EINVAL;
3382 ret = init_fs_context(dir_entry, ce, &context);
3383 if (ret)
3384 return ret;
3386 for (conn = find_first_fs (&context);
3387 conn != NULL;
3388 conn = find_next_fs (&context, conn, ret)) {
3390 ret = RXAFS_Link (conn->connection,
3391 &dir_entry->fid.fid,
3392 name,
3393 &existing_entry->fid.fid,
3394 &new_status,
3395 &status,
3396 &volsync);
3397 if (host_downp(ret)) {
3398 continue;
3399 } else if (ret) {
3400 free_fs_server_context (&context);
3401 arla_warn (ADEBFCACHE, ret, "Link");
3402 conn = NULL;
3403 goto out;
3405 conn_ref(conn);
3406 break;
3408 free_fs_server_context (&context);
3410 if (host_downp(ret)) {
3411 ret = ENETDOWN;
3412 goto out;
3415 update_entry (dir_entry, &status, NULL, &volsync,
3416 conn, ce->cred);
3418 update_entry (existing_entry, &new_status, NULL, NULL,
3419 conn, ce->cred);
3421 out:
3422 if (conn)
3423 conn_free(conn);
3424 AssertExclLocked(&dir_entry->lock);
3425 return ret;
3429 * Remove a file from a directory.
3433 remove_file (FCacheEntry *dir_entry, const char *name, CredCacheEntry *ce)
3435 int ret;
3436 ConnCacheEntry *conn;
3437 AFSFetchStatus status;
3438 AFSVolSync volsync;
3439 fs_server_context context;
3441 AssertExclLocked(&dir_entry->lock);
3443 if (connected_mode == CONNECTED) {
3445 ret = init_fs_context(dir_entry, ce, &context);
3446 if (ret)
3447 return ret;
3449 for (conn = find_first_fs (&context);
3450 conn != NULL;
3451 conn = find_next_fs (&context, conn, ret)) {
3453 ret = RXAFS_RemoveFile (conn->connection,
3454 &dir_entry->fid.fid,
3455 name,
3456 &status,
3457 &volsync);
3458 if (host_downp(ret)) {
3459 continue;
3460 } else if (ret) {
3461 free_fs_server_context (&context);
3462 arla_warn (ADEBFCACHE, ret, "RemoveFile");
3463 conn = NULL;
3464 goto out;
3466 conn_ref(conn);
3467 break;
3469 free_fs_server_context (&context);
3471 if (host_downp(ret))
3472 ret = ENETDOWN;
3474 } else {
3475 fbuf the_fbuf;
3476 VenusFid child_fid;
3477 int fd;
3479 status = dir_entry->status;
3481 conn = NULL;
3483 ret = fcache_get_fbuf (dir_entry, &fd, &the_fbuf,
3484 O_RDONLY, FBUF_READ|FBUF_SHARED);
3485 if (ret)
3486 goto out;
3488 ret = fdir_lookup(&the_fbuf, &dir_entry->fid, name, &child_fid);
3489 if (ret == 0) {
3490 FCacheEntry *child_entry = NULL;
3491 uint32_t disco_id = 0;
3493 ret = fcache_find(&child_entry, child_fid);
3494 if (ret == 0)
3495 disco_id = child_entry->disco_id;
3497 disco_id = disco_unlink(&dir_entry->fid, &child_fid,
3498 name, disco_id);
3500 if (child_entry) {
3501 child_entry->disco_id = disco_id;
3502 fcache_release(child_entry);
3504 ret = 0;
3507 fbuf_end (&the_fbuf);
3508 close (fd);
3511 if (ret == 0)
3512 update_entry (dir_entry, &status, NULL, &volsync,
3513 conn, ce->cred);
3515 out:
3516 if (conn)
3517 conn_free(conn);
3518 AssertExclLocked(&dir_entry->lock);
3519 return ret;
3523 * Remove a directory from a directory.
3527 remove_directory (FCacheEntry *dir_entry,
3528 const char *name,
3529 CredCacheEntry *ce)
3531 int ret;
3532 ConnCacheEntry *conn;
3533 AFSFetchStatus status;
3534 AFSVolSync volsync;
3535 fs_server_context context;
3537 AssertExclLocked(&dir_entry->lock);
3539 if (connected_mode != CONNECTED)
3540 return EINVAL;
3542 ret = init_fs_context(dir_entry, ce, &context);
3543 if (ret)
3544 return ret;
3546 for (conn = find_first_fs (&context);
3547 conn != NULL;
3548 conn = find_next_fs (&context, conn, ret)) {
3550 ret = RXAFS_RemoveDir (conn->connection,
3551 &dir_entry->fid.fid,
3552 name,
3553 &status,
3554 &volsync);
3555 if (host_downp(ret)) {
3556 continue;
3557 } else if (ret) {
3558 free_fs_server_context (&context);
3559 arla_warn (ADEBFCACHE, ret, "RemoveDir");
3560 conn = NULL;
3561 goto out;
3563 conn_ref(conn);
3564 break;
3566 free_fs_server_context (&context);
3568 if (host_downp(ret)) {
3569 ret = ENETDOWN;
3570 goto out;
3573 update_entry (dir_entry, &status, NULL, &volsync,
3574 conn, ce->cred);
3576 out:
3577 if (conn)
3578 conn_free(conn);
3579 AssertExclLocked(&dir_entry->lock);
3580 return ret;
3584 * Rename a file
3588 rename_file (FCacheEntry *old_dir,
3589 const char *old_name,
3590 FCacheEntry *new_dir,
3591 const char *new_name,
3592 CredCacheEntry *ce)
3594 int ret = ARLA_CALL_DEAD;
3595 ConnCacheEntry *conn;
3596 AFSFetchStatus orig_status, new_status;
3597 AFSVolSync volsync;
3598 fs_server_context context;
3600 AssertExclLocked(&old_dir->lock);
3601 AssertExclLocked(&new_dir->lock);
3603 if (connected_mode != CONNECTED)
3604 return EINVAL;
3606 ret = init_fs_context(old_dir, ce, &context);
3607 if (ret)
3608 return ret;
3610 for (conn = find_first_fs (&context);
3611 conn != NULL;
3612 conn = find_next_fs (&context, conn, ret)) {
3614 ret = RXAFS_Rename (conn->connection,
3615 &old_dir->fid.fid,
3616 old_name,
3617 &new_dir->fid.fid,
3618 new_name,
3619 &orig_status,
3620 &new_status,
3621 &volsync);
3622 if (host_downp(ret)) {
3623 continue;
3624 } else if (ret) {
3625 free_fs_server_context (&context);
3626 arla_warn (ADEBFCACHE, ret, "Rename");
3627 conn = NULL;
3628 goto out;
3630 conn_ref(conn);
3631 break;
3633 free_fs_server_context (&context);
3635 if (host_downp(ret)) {
3636 ret = ENETDOWN;
3637 goto out;
3640 update_entry (old_dir, &orig_status, NULL, &volsync,
3641 conn, ce->cred);
3643 update_entry (new_dir, &new_status, NULL, &volsync,
3644 conn, ce->cred);
3646 out:
3647 if (conn)
3648 conn_free(conn);
3649 AssertExclLocked(&old_dir->lock);
3650 AssertExclLocked(&new_dir->lock);
3651 return ret;
3655 * Return the fid to the root.
3659 getroot (VenusFid *res, CredCacheEntry *ce)
3661 VolCacheEntry *ve;
3662 VenusFid fid;
3663 const char *root_volume = volcache_get_rootvolume ();
3664 int ret;
3665 const char *this_cell = cell_getthiscell ();
3666 int32_t this_cell_id;
3668 if (dynroot_enablep()) {
3669 this_cell = "dynroot";
3670 this_cell_id = dynroot_cellid();
3671 } else {
3672 this_cell_id = cell_name2num (this_cell);
3673 if (this_cell_id == -1)
3674 arla_errx (1, ADEBERROR, "cell %s does not exist", this_cell);
3677 ret = volcache_getbyname (root_volume, this_cell_id, ce, &ve, NULL);
3678 if (ret) {
3679 arla_warn (ADEBWARN, ret,
3680 "Cannot find the root volume (%s) in cell %s",
3681 root_volume, this_cell);
3682 return ret;
3685 fid.Cell = this_cell_id;
3686 if (ve->entry.flags & VLF_ROEXISTS) {
3687 fid.fid.Volume = ve->entry.volumeId[ROVOL];
3688 } else if (ve->entry.flags & VLF_RWEXISTS) {
3689 arla_warnx(ADEBERROR,
3690 "getroot: %s in cell %s is missing a RO clone, not good",
3691 root_volume, this_cell);
3692 fid.fid.Volume = ve->entry.volumeId[RWVOL];
3693 } else {
3694 arla_errx(1, ADEBERROR,
3695 "getroot: %s in cell %s has no RW or RO clone?",
3696 root_volume, this_cell);
3698 fid.fid.Vnode = fid.fid.Unique = 1;
3700 volcache_free (ve);
3702 *res = fid;
3703 return 0;
3707 * Return the type for this volume.
3710 long
3711 getvoltype(int32_t volid, const VolCacheEntry *ve)
3713 int i;
3715 for (i = RWVOL; i <= BACKVOL; ++i)
3716 if (ve->entry.volumeId[i] == volid)
3717 return i;
3718 assert (FALSE);
3719 return -1; /* NOT REACHED */
3723 * Return the entry for `fid' or NULL.
3727 fcache_find (FCacheEntry **res, VenusFid fid)
3729 *res = find_entry_nolock (fid);
3730 if (*res == NULL)
3731 return ENOENT;
3732 ObtainWriteLock (&(*res)->lock);
3733 return 0;
3737 * Return the entry for `fid'. If it's not cached, add it.
3741 fcache_get (FCacheEntry **res, VenusFid fid, CredCacheEntry *ce)
3743 FCacheEntry *old;
3744 FCacheEntry *e;
3745 VolCacheEntry *vol;
3746 int i, error;
3748 *res = NULL;
3750 error = fcache_find(&old, fid);
3751 if (error == 0) {
3752 assert (old->flags.usedp);
3753 *res = old;
3754 return 0;
3757 error = volcache_getbyid (fid.fid.Volume, fid.Cell, ce, &vol, NULL);
3758 if (error) {
3759 if (connected_mode == DISCONNECTED && error == ENOENT)
3760 return ENETDOWN;
3761 return error;
3764 e = find_free_entry ();
3765 assert (e != NULL);
3767 error = fcache_find(&old, fid);
3768 if (error == 0) {
3769 AssertExclLocked(&e->lock);
3770 ReleaseWriteLock (&e->lock);
3772 e->lru_le = listaddtail (lrulist, e);
3773 assert(e->lru_le);
3775 assert (old->flags.usedp);
3776 *res = old;
3777 return 0;
3780 e->fid = fid;
3781 e->refcount = 0;
3782 e->host = 0;
3783 e->length = 0;
3784 e->wanted_length = 0;
3785 e->fetched_length = 0;
3786 memset (&e->status, 0, sizeof(e->status));
3787 memset (&e->callback, 0, sizeof(e->callback));
3788 memset (&e->volsync, 0, sizeof(e->volsync));
3789 for (i = 0; i < NACCESS; i++) {
3790 e->acccache[i].cred = ARLA_NO_AUTH_CRED;
3791 e->acccache[i].access = 0;
3793 e->anonaccess = 0;
3794 e->flags.usedp = TRUE;
3795 e->flags.attrp = FALSE;
3796 e->flags.attrusedp = FALSE;
3797 e->flags.datausedp = FALSE;
3798 e->flags.extradirp = FALSE;
3799 e->flags.mountp = FALSE;
3800 e->flags.fake_mp = FALSE;
3801 e->flags.vol_root = FALSE;
3802 e->flags.kernelp = FALSE;
3803 e->flags.sentenced = FALSE;
3804 e->flags.silly = FALSE;
3805 e->tokens = 0;
3806 memset (&e->parent, 0, sizeof(e->parent));
3807 e->lru_le = listaddhead (lrulist, e);
3808 assert(e->lru_le);
3809 e->invalid_ptr = -1;
3810 e->volume = vol;
3811 e->priority = fprio_get(fid);
3812 e->hits = 0;
3813 e->cleanergen = 0;
3815 hashtabadd (hashtab, e);
3817 *res = e;
3818 return 0;
3822 * Release the lock on `e' and mark it as stale if it has been sentenced.
3825 void
3826 fcache_release (FCacheEntry *e)
3828 AssertExclLocked(&e->lock);
3830 ReleaseWriteLock (&e->lock);
3832 if (e->flags.sentenced) {
3833 AFSCallBack broken_callback = {0, 0, CBDROPPED};
3835 stale (e, broken_callback);
3836 e->flags.sentenced = FALSE;
3844 static Bool
3845 uptodatep (FCacheEntry *e)
3847 struct timeval tv;
3848 assert (e->flags.usedp);
3850 if (connected_mode != CONNECTED &&
3851 connected_mode != FETCH_ONLY)
3852 return TRUE;
3854 gettimeofday(&tv, NULL);
3856 if (tv.tv_sec < e->callback.ExpirationTime &&
3857 e->callback.CallBackType != CBDROPPED &&
3858 (e->callback.CallBackType != 0
3859 || e->volume->volsync.spare1 != e->volsync.spare1))
3860 return TRUE;
3862 return FALSE;
3866 * The idea is that we start to stat everything after the prefered
3867 * entry, everything before that is probably not useful to get, the
3868 * user is probably trying to stat() everything _after_ that node.
3869 * This might be somewhat bogus, but we dont care (for now).
3872 struct bulkstat {
3873 int len; /* used entries in fids and names */
3874 AFSFid fids[AFSCBMAX]; /* fids to fetch */
3875 char *names[AFSCBMAX]; /* names it install */
3876 AFSFid *used; /* do we have a prefered node */
3877 CredCacheEntry *ce; /* cred to use */
3880 typedef union {
3881 struct nnpfs_message_installnode node;
3882 struct nnpfs_message_installattr attr;
3883 } nnpfs_message_install_node_attr;
3885 static int
3886 bulkstat_help_func (VenusFid *fid, const char *name, void *ptr)
3888 struct bulkstat *bs = (struct bulkstat *) ptr;
3889 AccessEntry *ae;
3890 FCacheEntry key;
3891 FCacheEntry *e;
3893 /* Is bs full ? */
3894 if (bs->len > fcache_bulkstatus_num)
3895 return 0;
3897 /* Ignore . and .. */
3898 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
3899 return 0;
3902 * Do we have a prefered node, and is this the one. If we don't know
3903 * the name of the node (ie bs.names[0] == NULL), fill it in.
3904 * Set bs->used to NULL it indicate that we should start stat stuff
3905 * from here, remeber that bs->len == 1 if bs->used is set.
3907 if (bs->used) {
3908 if (memcmp(bs->used, &fid->fid, sizeof(fid->fid)) == 0) {
3909 if (bs->names[0] == NULL)
3910 bs->names[0] = strdup (name);
3911 bs->used = NULL; /* stat everything after this */
3913 return 0;
3917 * Already cached for this pag ?
3919 key.fid = *fid;
3920 e = (FCacheEntry *)hashtabsearch (hashtab, (void *)&key);
3921 if (e
3922 && e->flags.usedp
3923 && e->flags.attrp
3924 && uptodatep (e)
3925 && findaccess (bs->ce->cred, e->acccache, &ae) == TRUE) {
3926 arla_warnx (ADEBFCACHE,
3927 "bulkstat_help_func: already cached "
3928 "(%d.%d.%d.%d) name: %s",
3929 fid->Cell, fid->fid.Volume, fid->fid.Vnode,
3930 fid->fid.Unique, name);
3931 return 0;
3934 if (fcache_enable_bulkstatus == 2) {
3935 /* cache the name for the installnode */
3936 bs->names[bs->len] = strdup (name);
3937 if (bs->names[bs->len] == NULL)
3938 return 0;
3939 } else {
3940 bs->names[bs->len] = NULL;
3944 bs->fids[bs->len] = fid->fid;
3945 bs->len++;
3947 return 0;
3951 * Do bulkstat for ``parent_entry''. Make sure that ``prefered_entry''
3952 * is in the list of fids it not NULL, and it ``prefered_name'' is NULL
3953 * try to find it in the list files in the directory.
3955 * Entry Success Failure
3956 * parent_entry locked locked locked
3957 * prefered_entry locked locked locked
3958 * or if NULL if set to NULL must not be locked
3959 * prefered_fid related fcache-entry must not be locked
3960 * ce not NULL
3963 static int
3964 get_attr_bulk (FCacheEntry *parent_entry,
3965 FCacheEntry *prefered_entry,
3966 VenusFid *prefered_fid,
3967 const char *prefered_name,
3968 CredCacheEntry *ce)
3970 fs_server_context context;
3971 ConnCacheEntry *conn = NULL;
3972 struct bulkstat bs;
3973 AFSBulkStats stats;
3974 AFSVolSync sync;
3975 AFSCBFids fids;
3976 fbuf the_fbuf;
3977 int ret, fd;
3978 AFSCBs cbs;
3979 int i;
3980 int len;
3981 struct collect_stat collectstat;
3983 arla_warnx (ADEBFCACHE, "get_attr_bulk");
3985 AssertExclLocked(&parent_entry->lock);
3986 if (prefered_entry)
3987 AssertExclLocked(&prefered_entry->lock);
3989 if (fcache_enable_bulkstatus == 0)
3990 return -1;
3992 if (parent_entry->length == 0) {
3993 arla_warnx (ADEBFCACHE, "get_attr_bulk: parent doesn't have data");
3994 return -1;
3997 fids.val = bs.fids;
3999 memset (bs.names, 0, sizeof(bs.names));
4000 memset (bs.fids, 0, sizeof(bs.fids));
4001 bs.len = 0;
4002 bs.ce = ce;
4003 bs.used = NULL;
4006 * If we have a prefered_entry, and that to the first entry in the
4007 * array. This is used later. If we find the prefered_entry in the
4008 * directory-structure its ignored.
4011 if (prefered_fid) {
4012 arla_warnx (ADEBFCACHE, "get_attr_bulk: using prefered_entry");
4013 bs.used = &prefered_fid->fid;
4014 fids.val[bs.len] = prefered_fid->fid;
4015 if (prefered_name != NULL) {
4016 bs.names[bs.len] = strdup(prefered_name);
4017 if (bs.names[bs.len] == NULL)
4018 return ENOMEM;
4019 } else {
4020 bs.names[bs.len] = NULL;
4022 bs.len++;
4025 ret = fcache_get_fbuf (parent_entry, &fd, &the_fbuf,
4026 O_RDONLY, FBUF_READ|FBUF_SHARED);
4027 if (ret)
4028 return ret;
4030 ret = fdir_readdir (&the_fbuf,
4031 bulkstat_help_func,
4032 &bs,
4033 parent_entry->fid,
4034 NULL);
4035 fbuf_end (&the_fbuf);
4036 close (fd);
4037 if (ret)
4038 goto out_names;
4040 fids.len = bs.len;
4043 * Don't do BulkStatus when fids.len == 0 since we should never do it.
4044 * There should at least be the node that we want in the BulkStatus.
4047 if (fids.len == 0) {
4048 if (prefered_fid)
4049 arla_warnx (ADEBERROR,
4050 "get_attr_bulk: "
4051 "prefered_fid not found in dir");
4052 /* XXX MAGIC send it back so we don't do it again soon */
4053 parent_entry->hits -= 64;
4054 ret = EINVAL;
4055 goto out_names;
4059 * XXX if there is a prefered fid, and and we didn't find the name for it
4060 * return an error.
4063 if (prefered_fid && bs.names[0] == NULL) {
4064 arla_warnx (ADEBFCACHE,
4065 "get_attr_bulk: didn't find prefered_fid's name");
4066 ret = EINVAL;
4067 goto out_names;
4070 ret = ARLA_CALL_DEAD;
4072 ret = init_fs_context(parent_entry, ce, &context);
4073 if (ret)
4074 return ret;
4076 for (conn = find_first_fs (&context);
4077 conn != NULL;
4078 conn = find_next_fs (&context, conn, ret)) {
4080 stats.val = NULL;
4081 cbs.val = NULL;
4082 stats.len = cbs.len = 0;
4084 collectstats_start(&collectstat);
4085 ret = RXAFS_BulkStatus (conn->connection, &fids, &stats, &cbs, &sync);
4086 collectstats_stop(&collectstat, parent_entry, conn,
4087 find_partition(&context),
4088 arla_STATISTICS_REQTYPE_BULKSTATUS, fids.len);
4089 if (ret) {
4090 free (stats.val);
4091 free (cbs.val);
4094 if (host_downp(ret)) {
4095 continue;
4096 } else if (ret) {
4097 free_fs_server_context(&context);
4098 arla_warn(ADEBFCACHE, ret, "BulkStatus");
4099 conn = NULL;
4100 goto out_names;
4102 conn_ref(conn);
4103 break;
4106 free_fs_server_context (&context);
4108 if (ret) {
4109 ret = ENETDOWN;
4110 goto out_names;
4113 arla_warnx (ADEBFCACHE,"get_attr_bulk: BulkStatus returned %d",ret);
4115 len = min(fids.len, min(stats.len, cbs.len));
4118 * Save results of bulkstatus
4121 if (ret == 0) {
4122 FCacheEntry *e;
4123 VenusFid fid;
4125 fcache_counter.fetch_attr_bulk += len;
4127 fid.Cell = parent_entry->fid.Cell;
4128 for (i = 0; i < len && ret == 0; i++) {
4130 fid.fid = fids.val[i];
4132 if (VenusFid_cmp(prefered_fid, &fid) == 0) {
4133 e = prefered_entry;
4134 } else {
4135 e = find_entry_nolock (fid);
4136 if (e != NULL && CheckLock(&e->lock) != 0)
4137 continue;
4139 ret = fcache_get (&e, fid, ce);
4140 if (ret)
4141 break;
4143 update_attr_entry (e,
4144 &stats.val[i],
4145 &cbs.val[i],
4146 &sync,
4147 conn,
4148 ce->cred);
4149 e->parent = parent_entry->fid;
4150 if (prefered_entry != e) {
4151 fcache_release(e);
4157 * Insert result into kernel
4160 if (fcache_enable_bulkstatus == 2 && ret == 0) {
4161 nnpfs_message_install_node_attr msg[AFSCBMAX];
4162 struct nnpfs_msg_node *node;
4163 nnpfs_handle *parent;
4164 FCacheEntry *e;
4165 VenusFid fid;
4166 int j;
4168 fid.Cell = parent_entry->fid.Cell;
4169 for (i = 0 , j = 0; i < len && ret == 0; i++) {
4170 u_int tokens;
4172 fid.fid = fids.val[i];
4174 if (VenusFid_cmp(prefered_fid, &fid) == 0) {
4175 e = prefered_entry;
4176 } else {
4177 e = find_entry_nolock (fid);
4178 if (e != NULL && CheckLock(&e->lock) != 0)
4179 continue;
4181 ret = fcache_get (&e, fid, ce);
4182 if (ret)
4183 break;
4187 arla_warnx (ADEBFCACHE, "installing %d.%d.%d\n",
4188 e->fid.fid.Volume,
4189 e->fid.fid.Vnode,
4190 e->fid.fid.Unique);
4191 assert_flag(e,kernelp);
4192 e->flags.attrusedp = TRUE;
4195 * Its its already installed, just update with installattr
4198 e->tokens |= NNPFS_ATTR_R;
4199 tokens = e->tokens;
4200 if (!e->flags.kernelp || !e->flags.datausedp)
4201 tokens &= ~NNPFS_DATA_MASK;
4203 if (e->flags.kernelp) {
4204 msg[j].attr.header.opcode = NNPFS_MSG_INSTALLATTR;
4205 node = &msg[j].attr.node;
4206 parent = NULL;
4207 } else {
4208 msg[j].node.header.opcode = NNPFS_MSG_INSTALLNODE;
4209 node = &msg[j].node.node;
4210 parent = &msg[j].node.parent_handle;
4211 e->flags.kernelp = TRUE;
4212 strlcpy (msg[j].node.name, bs.names[i],
4213 sizeof(msg[j].node.name));
4215 node->tokens = tokens;
4218 * Don't install symlink since they might be
4219 * mount-points.
4222 if (e->status.FileType != TYPE_LINK) {
4223 fcacheentry2nnpfsnode (&e->fid,
4224 &e->fid,
4225 &stats.val[i],
4226 node,
4227 parent_entry->acccache,
4228 FCACHE2NNPFSNODE_ALL);
4230 if (parent)
4231 *parent = *(struct nnpfs_handle*) &parent_entry->fid;
4232 j++;
4234 if (prefered_entry != e)
4235 fcache_release(e);
4239 * Install if there is no error and we have something to install
4242 if (ret == 0 && j != 0)
4243 ret = nnpfs_send_message_multiple_list (kernel_fd,
4244 (struct nnpfs_message_header *) msg,
4245 sizeof (msg[0]),
4247 /* We have what we wanted, ignore errors */
4248 if (ret && i > 0 && prefered_entry)
4249 ret = 0;
4252 free (stats.val);
4253 free (cbs.val);
4255 out_names:
4256 for (i = 0 ; i < bs.len && ret == 0; i++)
4257 free (bs.names[i]);
4259 if (conn)
4260 conn_free(conn);
4262 arla_warnx (ADEBFCACHE, "get_attr_bulk: returned %d", ret);
4264 return ret;
4269 * fetch attributes for the note `entry' with the rights `ce'. If
4270 * `parent_entry' is not NULL, it is used for doing bulkstatus when
4271 * guess is necessary. If there is a named associated with `entry' it
4272 * should be filled into `prefered_name' as that will be used for
4273 * guessing that nodes should be bulkstat:ed.
4275 * If there is no bulkstatus done, a plain FetchStatus is done.
4279 fcache_verify_attr (FCacheEntry *entry, FCacheEntry *parent,
4280 const char *prefered_name, CredCacheEntry* ce)
4282 AccessEntry *ae;
4284 if (dynroot_is_dynrootp (entry))
4285 return dynroot_get_attr (entry, ce);
4287 if (entry->flags.usedp
4288 && entry->flags.attrp
4289 && uptodatep(entry)
4290 && findaccess (ce->cred, entry->acccache, &ae) == TRUE)
4292 arla_warnx (ADEBFCACHE, "fcache_get_attr: have attr");
4293 fcache_counter.fetch_attr_cached++;
4294 return 0;
4298 * XXX is this right ?
4299 * Dont ask fileserver if this file is deleted
4301 if (entry->flags.silly) {
4302 entry->tokens |= NNPFS_ATTR_R;
4303 entry->flags.attrp = TRUE;
4304 return 0;
4307 if (connected_mode == DISCONNECTED) {
4308 if (entry->flags.attrp) {
4309 AccessEntry *ae;
4310 findaccess(ce->cred, entry->acccache, &ae);
4311 ae->cred = ce->cred;
4312 ae->access = 0x7f; /* XXXDISCO */
4313 return 0;
4315 else
4316 return ENETDOWN;
4320 * If there is no parent, `entry' is a root-node, or the parent is
4321 * un-initialized, don't bother bulkstatus.
4323 if (parent != NULL
4324 && entry->fid.fid.Vnode != 1
4325 && entry->fid.fid.Unique != 1
4326 && !entry->flags.mountp
4327 && !entry->flags.fake_mp
4328 && entry->parent.Cell != 0
4329 && entry->parent.fid.Volume != 0
4330 && entry->parent.fid.Vnode != 0
4331 && entry->parent.fid.Unique != 0)
4334 * Check if the entry is used, that means that
4335 * there is greater chance that we we'll succeed
4336 * when doing bulkstatus.
4339 if (parent->hits++ > fcache_bulkstatus_num &&
4340 parent->flags.datausedp) {
4341 int error;
4343 arla_warnx (ADEBFCACHE, "fcache_get_attr: doing bulk get_attr");
4345 error = get_attr_bulk (parent,
4346 entry, &entry->fid,
4347 prefered_name, ce);
4348 /* magic calculation when we are going to do next bulkstat */
4349 parent->hits = 0;
4351 if (error == 0)
4352 return 0;
4357 * We got here because the bulkstatus failed, didn't want to do a
4358 * bulkstatus or we didn't get a parent for the entry
4361 arla_warnx (ADEBFCACHE, "fcache_get_attr: doing read_attr");
4363 return read_attr (entry, ce);
4369 * Make sure that `e' has attributes and that they are up-to-date.
4370 * `e' must be write-locked.
4374 static int
4375 do_read_data (FCacheEntry *e, CredCacheEntry *ce)
4377 int ret = ARLA_CALL_DEAD;
4378 fs_server_context context;
4379 ConnCacheEntry *conn;
4381 if (connected_mode == DISCONNECTED)
4382 return ENETDOWN;
4384 ret = init_fs_context(e, ce, &context);
4385 if (ret)
4386 return ret;
4388 for (conn = find_first_fs (&context);
4389 conn != NULL;
4390 conn = find_next_fs (&context, conn, ret)) {
4391 ret = read_data (e, conn, ce, find_partition(&context));
4392 if (!try_next_fs (ret, &e->fid))
4393 break;
4395 free_fs_server_context (&context);
4397 if (host_downp(ret))
4398 ret = ENETDOWN;
4399 return ret;
4403 * Make sure that `e' has file data and is up-to-date.
4407 fcache_verify_data (FCacheEntry *e, CredCacheEntry *ce)
4409 ConnCacheEntry *conn = NULL;
4410 int ret;
4411 fs_server_context context;
4413 assert (e->flags.usedp);
4414 AssertExclLocked(&e->lock);
4416 if (dynroot_is_dynrootp (e))
4417 return dynroot_get_data (e, ce);
4419 /* Don't get data for deleted files */
4420 if (e->flags.silly)
4421 return 0;
4423 if (e->flags.attrp && uptodatep(e)) {
4424 if (e->wanted_length <= e->fetched_length) {
4425 fcache_counter.fetch_data_cached++;
4426 return 0;
4427 } else
4428 return do_read_data (e, ce);
4429 } else {
4430 ret = do_read_attr (e, ce, &conn, &context);
4431 if (ret)
4432 return ret;
4433 if (e->wanted_length <= e->fetched_length) {
4434 fcache_counter.fetch_data_cached++;
4435 free_fs_server_context (&context);
4436 return 0;
4439 ret = read_data (e, conn, ce, find_partition(&context));
4440 free_fs_server_context (&context);
4441 return ret;
4445 * Fetch `fid' with data, returning the cache entry in `res'.
4446 * note that `fid' might change.
4450 fcache_get_data (FCacheEntry **e, CredCacheEntry **ce,
4451 uint64_t wanted_length)
4453 int ret;
4455 if ((*e)->flags.fake_mp) {
4456 VenusFid new_fid;
4457 FCacheEntry *new_root;
4459 ret = resolve_mp (e, &new_fid, ce);
4460 if (ret) {
4461 return ret;
4463 ret = fcache_get (&new_root, new_fid, *ce);
4464 if (ret) {
4465 return ret;
4467 ret = fcache_verify_attr (new_root, NULL, NULL, *ce);
4468 if (ret) {
4469 fcache_release (new_root);
4470 return ret;
4472 (*e)->flags.fake_mp = FALSE;
4473 (*e)->flags.mountp = TRUE;
4474 (*e)->status.FileType = TYPE_LINK;
4475 update_fid ((*e)->fid, *e, new_fid, new_root);
4476 fcache_release (*e);
4477 *e = new_root;
4478 install_attr (*e, FCACHE2NNPFSNODE_ALL);
4481 if (wanted_length) {
4482 (*e)->wanted_length = wanted_length;
4483 } else {
4485 * XXX remove this case, attr should either be known already
4486 * here, or we should just fetch `whole file'/next block.
4489 ret = fcache_verify_attr (*e, NULL, NULL, *ce);
4490 if (ret) {
4491 return ret;
4493 if ((*e)->length == 0 || !uptodatep(*e)) {
4494 (*e)->wanted_length = fcache_get_status_length(&(*e)->status);
4498 ret = fcache_verify_data (*e, *ce);
4499 return ret;
4503 * Helper function for followmountpoint.
4504 * Given the contents of a mount-point, figure out the cell and volume name.
4506 * ``mp'' must be writeable and should not be used afterwards.
4507 * ``*volname'' is a pointer to somewhere in the mp string.
4508 * ``cell'' should be set before function is called to default cell.
4511 static int
4512 parse_mountpoint (char *mp, size_t len, int32_t *cell, char **volname)
4514 char *colon;
4516 mp[len - 1] = '\0';
4517 colon = strchr (mp, ':');
4518 if (colon != NULL) {
4519 *colon++ = '\0';
4520 *cell = cell_name2num (mp + 1);
4521 if (*cell == -1)
4522 return ENOENT;
4523 *volname = colon;
4524 } else {
4525 *volname = mp + 1;
4527 return 0;
4531 * Used by followmountpoint to figure out what clone of a volume
4532 * should be used.
4534 * Given a `volname', `cell', it uses the given `ce', `mount_symbol'
4535 * and `parent_type' to return a volume id in `volume'.
4537 * The rules are:
4539 * "readonly" -> RO
4540 * BK + "backup" -> fail
4541 * "backup" -> BK
4542 * BK + "" + # -> RO
4543 * RO + "" + # -> RO
4544 * * -> RW
4546 * this_type = "" | "readonly" | "backup"
4547 * parent_type = RW | RO | BK
4548 * mount_symbol = "#" | "%"
4551 static int
4552 find_volume (const char *volname, int32_t cell,
4553 CredCacheEntry *ce, char mount_symbol, int parent_type,
4554 uint32_t *volid, VolCacheEntry **ve)
4556 int result_type;
4557 int this_type;
4558 int res;
4560 res = volcache_getbyname (volname, cell, ce, ve, &this_type);
4561 if (res)
4562 return res;
4564 assert (this_type == RWVOL ||
4565 this_type == ROVOL ||
4566 this_type == BACKVOL);
4568 if (this_type == ROVOL) {
4569 if (!((*ve)->entry.flags & VLF_ROEXISTS)) {
4570 volcache_free (*ve);
4571 return ENOENT;
4573 result_type = ROVOL;
4574 } else if (this_type == BACKVOL && parent_type == BACKVOL) {
4575 volcache_free (*ve);
4576 return ENOENT;
4577 } else if (this_type == BACKVOL) {
4578 if (!((*ve)->entry.flags & VLF_BOEXISTS)) {
4579 volcache_free (*ve);
4580 return ENOENT;
4582 result_type = BACKVOL;
4583 } else if (this_type == RWVOL &&
4584 parent_type != RWVOL &&
4585 mount_symbol == '#') {
4586 if ((*ve)->entry.flags & VLF_ROEXISTS)
4587 result_type = ROVOL;
4588 else if ((*ve)->entry.flags & VLF_RWEXISTS)
4589 result_type = RWVOL;
4590 else {
4591 volcache_free (*ve);
4592 return ENOENT;
4594 } else {
4595 if ((*ve)->entry.flags & VLF_RWEXISTS)
4596 result_type = RWVOL;
4597 else if ((*ve)->entry.flags & VLF_ROEXISTS)
4598 result_type = ROVOL;
4599 else {
4600 volcache_free (*ve);
4601 return ENOENT;
4604 *volid = (*ve)->entry.volumeId[result_type];
4605 return 0;
4609 * Set `fid' to point to the root of the volume pointed to by the
4610 * mount-point in (buf, len).
4612 * If succesful, `fid' will be updated to the root of the volume, and
4613 * `ce' will point to a cred in the new cell.
4616 static int
4617 get_root_of_volume (VenusFid *fid, const VenusFid *parent,
4618 VolCacheEntry *volume,
4619 CredCacheEntry **ce,
4620 char *buf, size_t len)
4622 VenusFid oldfid = *fid;
4623 char *volname;
4624 int32_t cell;
4625 uint32_t volid;
4626 int res;
4627 long parent_type, voltype;
4628 char mount_symbol;
4629 VolCacheEntry *ve;
4630 FCacheEntry *e;
4632 cell = fid->Cell;
4634 res = parse_mountpoint (buf, len, &cell, &volname);
4635 if (res)
4636 return res;
4639 * If this is a cross-cell mountpoint we need new credentials.
4642 if ((*ce)->cell != cell) {
4643 CredCacheEntry *new_ce;
4645 new_ce = cred_get(cell, (*ce)->cred, CRED_ANY);
4646 if (new_ce == NULL)
4647 return ENOMEM;
4648 cred_free (*ce);
4649 *ce = new_ce;
4652 parent_type = getvoltype (fid->fid.Volume, volume);
4653 mount_symbol = *buf;
4655 res = find_volume (volname, cell, *ce, mount_symbol,
4656 parent_type, &volid, &ve);
4657 if (res)
4658 return res;
4661 * Create the new fid. The root of a volume always has
4662 * (Vnode, Unique) = (1,1)
4665 fid->Cell = cell;
4666 fid->fid.Volume = volid;
4667 fid->fid.Vnode = fid->fid.Unique = 1;
4670 * Check if we are looking up ourself, if we are, just return.
4673 if (VenusFid_cmp(fid, parent) == 0) {
4674 volcache_free (ve);
4675 return 0;
4678 res = fcache_get (&e, *fid, *ce);
4679 if (res) {
4680 volcache_free (ve);
4681 return res;
4685 * Root nodes are a little bit special. We keep track of
4686 * their parent in `parent' so that `..' can be handled
4687 * properly.
4690 e->flags.vol_root = TRUE;
4691 e->parent = *parent;
4693 voltype = getvoltype (fid->fid.Volume, ve);
4694 if (ve->parent[voltype].volume == NULL) {
4695 ve->parent[voltype].fid = *parent;
4696 ve->parent[voltype].mp_fid = oldfid;
4698 volcache_volref (ve, volume, voltype);
4699 fcache_release (e);
4700 volcache_free (ve);
4701 return 0;
4705 * If this entry is a mount point, set the fid data to
4706 * the root directory of the volume it's pointing at,
4707 * otherwise just leave it.
4709 * Mount points are symbol links with the following contents:
4711 * '#' | '%' [ cell ':' ] volume-name [ '.' ]
4713 * This function tries to do a minimal amount of work. It always has
4714 * to fetch the attributes of `fid' and if it's a symbolic link, the
4715 * contents as well.
4719 followmountpoint (VenusFid *fid, const VenusFid *parent, FCacheEntry *parent_e,
4720 CredCacheEntry **ce)
4722 FCacheEntry *e;
4723 int ret;
4726 * Get the node for `fid' and verify that it's a symbolic link
4727 * with the correct bits. Otherwise, just return the old
4728 * `fid' without any change.
4731 ret = fcache_get (&e, *fid, *ce);
4732 if (ret)
4733 return ret;
4735 e->parent = *parent;
4736 ret = fcache_verify_attr (e, parent_e, NULL, *ce);
4737 if (ret) {
4738 fcache_release(e);
4739 return ret;
4742 if (e->flags.mountp)
4743 ret = resolve_mp (&e, fid, ce);
4745 fcache_release(e);
4746 return ret;
4750 * actually resolve a mount-point
4753 static int
4754 resolve_mp (FCacheEntry **e, VenusFid *ret_fid, CredCacheEntry **ce)
4756 VenusFid fid = (*e)->fid;
4757 int ret;
4758 fbuf the_fbuf;
4759 char *buf;
4760 int fd;
4761 uint32_t length;
4763 assert ((*e)->flags.fake_mp || (*e)->flags.mountp);
4764 AssertExclLocked(&(*e)->lock);
4766 (*e)->wanted_length = fcache_get_status_length(&(*e)->status);
4768 ret = fcache_verify_data (*e, *ce);
4769 if (ret)
4770 return ret;
4772 length = fcache_get_status_length(&(*e)->status);
4774 fd = fcache_open_file (*e, O_RDONLY);
4775 if (fd < 0)
4776 return errno;
4778 ret = fbuf_create (&the_fbuf, fd, length,
4779 FBUF_READ|FBUF_WRITE|FBUF_PRIVATE);
4780 if (ret) {
4781 close (fd);
4782 return ret;
4784 buf = fbuf_buf (&the_fbuf);
4786 ret = get_root_of_volume (&fid, &(*e)->parent, (*e)->volume,
4787 ce, buf, length);
4789 fbuf_end (&the_fbuf);
4790 close (fd);
4791 if (ret)
4792 return ret;
4793 *ret_fid = fid;
4794 return 0;
4801 static Bool
4802 print_entry (void *ptr, void *arg)
4804 FCacheEntry *e = (FCacheEntry *)ptr;
4806 arla_log(ADEBVLOG, "(%d, %u, %u, %u)" "%s%s%s%s"
4807 "%s%s%s%s" "%s%s%s%s length: %llu",
4808 e->fid.Cell,
4809 e->fid.fid.Volume, e->fid.fid.Vnode, e->fid.fid.Unique,
4811 e->flags.usedp?" used":"",
4812 e->flags.attrp?" attr":"",
4813 e->length != 0 ?" data":"",
4814 e->flags.attrusedp?" attrused":"",
4816 e->flags.datausedp?" dataused":"",
4817 e->flags.extradirp?" extradir":"",
4818 e->flags.mountp?" mount":"",
4819 e->flags.kernelp?" kernel":"",
4821 e->flags.sentenced?" sentenced":"",
4822 e->flags.silly?" silly":"",
4823 e->flags.fake_mp ? " fake mp" : "",
4824 e->flags.vol_root ? " vol root" : "",
4825 (unsigned long long)fcache_get_status_length(&e->status));
4826 return FALSE;
4834 void
4835 fcache_status (void)
4837 arla_log(ADEBVLOG, "%lu (%lu-/%lu)-%lu) files"
4838 "%lu (%lu-%lu) bytes\n",
4839 usedvnodes, lowvnodes, current_vnodes, highvnodes,
4840 (long)usedbytes, (long)lowbytes, (long)highbytes);
4841 hashtabforeach (hashtab, print_entry, NULL);
4848 void
4849 fcache_update_length (FCacheEntry *e, uint64_t len, uint64_t have_len)
4851 AssertExclLocked(&e->lock);
4853 assert (len >= e->length || e->length - len <= usedbytes);
4854 assert (have_len <= len);
4856 usedbytes = usedbytes - e->length + len;
4857 e->length = len;
4858 e->wanted_length = min(have_len,len);
4859 e->fetched_length = have_len;
4863 * Request an ACL and put it in opaque
4867 getacl(VenusFid fid,
4868 CredCacheEntry *ce,
4869 AFSOpaque *opaque)
4871 FCacheEntry *dire;
4872 ConnCacheEntry *conn;
4873 AFSFetchStatus status;
4874 AFSVolSync volsync;
4875 int ret;
4876 fs_server_context context;
4878 opaque->val = NULL;
4879 opaque->len = 0;
4881 if (connected_mode != CONNECTED)
4882 return EINVAL;
4884 ret = fcache_get (&dire, fid, ce);
4885 if (ret) {
4886 arla_warn (ADEBFCACHE, ret, "fcache_get");
4887 return ret;
4890 ret = init_fs_context(dire, ce, &context);
4891 if (ret)
4892 return ret;
4894 for (conn = find_first_fs (&context);
4895 conn != NULL;
4896 conn = find_next_fs (&context, conn, ret)) {
4898 ret = RXAFS_FetchACL (conn->connection, &fid.fid,
4899 opaque, &status, &volsync);
4900 if (ret) {
4901 free(opaque->val);
4902 opaque->val = NULL;
4903 opaque->len = 0;
4906 if (!try_next_fs (ret, &fid))
4907 break;
4909 if (ret)
4910 arla_warn (ADEBFCACHE, ret, "FetchACL");
4912 if (ret == 0)
4913 update_entry (dire, &status, NULL, &volsync,
4914 conn, ce->cred);
4915 else if (host_downp(ret))
4916 ret = ENETDOWN;
4918 free_fs_server_context (&context);
4919 fcache_release (dire);
4920 return ret;
4924 * Store the ACL read from opaque
4926 * If the function return 0, ret_e is set to the dir-entry and must
4927 * be fcache_released().
4931 setacl(VenusFid fid,
4932 CredCacheEntry *ce,
4933 AFSOpaque *opaque,
4934 FCacheEntry **ret_e)
4936 FCacheEntry *dire;
4937 ConnCacheEntry *conn;
4938 AFSFetchStatus status;
4939 AFSVolSync volsync;
4940 int ret;
4941 fs_server_context context;
4943 if (connected_mode != CONNECTED)
4944 return EINVAL;
4946 ret = fcache_get (&dire, fid, ce);
4947 if (ret) {
4948 arla_warn (ADEBFCACHE, ret, "fcache_get");
4949 return EINVAL;
4952 ret = init_fs_context(dire, ce, &context);
4953 if (ret)
4954 return ret;
4956 for (conn = find_first_fs (&context);
4957 conn != NULL;
4958 conn = find_next_fs (&context, conn, ret)) {
4959 ret = RXAFS_StoreACL (conn->connection, &fid.fid,
4960 opaque, &status, &volsync);
4961 if (!try_next_fs (ret, &fid))
4962 break;
4964 if (ret)
4965 arla_warn (ADEBFCACHE, ret, "StoreACL");
4967 if (ret == 0)
4968 update_entry (dire, &status, NULL, &volsync,
4969 conn, ce->cred);
4970 else if (host_downp(ret))
4971 ret = ENETDOWN;
4973 free_fs_server_context (&context);
4975 if (ret == 0) {
4976 *ret_e = dire;
4977 } else {
4978 *ret_e = NULL;
4979 fcache_release (dire);
4981 return ret;
4985 * Request volume status
4989 getvolstat(VenusFid fid, CredCacheEntry *ce,
4990 AFSFetchVolumeStatus *volstat,
4991 char *volumename, size_t volumenamesz,
4992 char *offlinemsg,
4993 char *motd)
4995 FCacheEntry *dire;
4996 ConnCacheEntry *conn;
4997 int ret;
4998 fs_server_context context;
5000 if (connected_mode != CONNECTED)
5001 return EINVAL;
5003 ret = fcache_get (&dire, fid, ce);
5004 if (ret) {
5005 arla_warn (ADEBFCACHE, ret, "fcache_get");
5006 return EINVAL;
5009 ret = init_fs_context(dire, ce, &context);
5010 if (ret)
5011 return ret;
5013 for (conn = find_first_fs (&context);
5014 conn != NULL;
5015 conn = find_next_fs (&context, conn, ret)) {
5016 ret = RXAFS_GetVolumeStatus (conn->connection, fid.fid.Volume,
5017 volstat, volumename, offlinemsg,
5018 motd);
5019 if (!try_next_fs (ret, &fid))
5020 break;
5022 if (ret)
5023 arla_warn (ADEBFCACHE, ret, "GetVolumeStatus");
5024 free_fs_server_context (&context);
5025 if (host_downp(ret))
5026 ret = ENETDOWN;
5027 if (ret == 0 && volumename[0] == '\0') {
5028 if (volcache_getname (fid.fid.Volume, fid.Cell,
5029 volumename, volumenamesz) == -1)
5030 strlcpy(volumename, "<unknown>", volumenamesz);
5033 fcache_release (dire);
5034 return ret;
5038 * Store volume status
5042 setvolstat(VenusFid fid, CredCacheEntry *ce,
5043 AFSStoreVolumeStatus *volstat,
5044 char *volumename,
5045 char *offlinemsg,
5046 char *motd)
5048 FCacheEntry *dire;
5049 ConnCacheEntry *conn;
5050 int ret;
5051 fs_server_context context;
5053 if (connected_mode != CONNECTED)
5054 return EINVAL;
5056 ret = fcache_get (&dire, fid, ce);
5057 if (ret) {
5058 arla_warn (ADEBFCACHE, ret, "fcache_get");
5059 return EINVAL;
5062 ret = init_fs_context(dire, ce, &context);
5063 if (ret)
5064 return ret;
5066 for (conn = find_first_fs (&context);
5067 conn != NULL;
5068 conn = find_next_fs (&context, conn, ret)) {
5069 ret = RXAFS_SetVolumeStatus (conn->connection, fid.fid.Volume,
5070 volstat, volumename, offlinemsg,
5071 motd);
5072 if (!try_next_fs (ret, &fid))
5073 break;
5075 if (ret) {
5076 if (host_downp(ret))
5077 ret = ENETDOWN;
5078 arla_warn (ADEBFCACHE, ret, "SetVolumeStatus");
5080 free_fs_server_context (&context);
5082 fcache_release (dire);
5083 return ret;
5087 * Get `fbuf' from `centry' that is opened with openflags
5088 * `open_flags' and fbuf flags with `fbuf_flags'
5090 * Assume that data is valid and `centry' is exclusive locked.
5094 fcache_get_fbuf (FCacheEntry *centry, int *fd, fbuf *fbuf,
5095 int open_flags, int fbuf_flags)
5097 int ret;
5098 unsigned len;
5099 struct stat sb;
5101 AssertExclLocked(&centry->lock);
5103 *fd = fcache_open_file (centry, open_flags);
5104 if (*fd < 0)
5105 return errno;
5107 if (fstat (*fd, &sb)) {
5108 ret = errno;
5109 close (*fd);
5110 return ret;
5113 len = sb.st_size;
5115 ret = fbuf_create (fbuf, *fd, len, fbuf_flags);
5116 if (ret) {
5117 close (*fd);
5118 return ret;
5120 return 0;
5127 static Bool
5128 sum_node (List *list, Listitem *li, void *arg)
5130 int64_t *a = arg;
5131 FCacheEntry *e = listdata (li);
5133 *a += e->length;
5135 return FALSE;
5139 int64_t
5140 fcache_calculate_usage (void)
5142 int64_t size = 0;
5144 listiter (lrulist, sum_node, &size);
5146 return size;
5153 const VenusFid *
5154 fcache_realfid (const FCacheEntry *entry)
5156 if (entry->flags.vol_root
5157 || (entry->fid.fid.Vnode == 1 && entry->fid.fid.Unique == 1)) {
5158 long voltype = getvoltype(entry->fid.fid.Volume, entry->volume);
5159 return &entry->volume->parent[voltype].mp_fid;
5160 } else {
5161 return &entry->fid;
5169 static Bool
5170 check_dir (List *list, Listitem *li, void *arg)
5172 FCacheEntry *e = listdata (li);
5173 fbuf the_fbuf;
5174 int fd, ret;
5176 if (CheckLock(&e->lock) != 0)
5177 return FALSE;
5179 ObtainWriteLock (&e->lock);
5181 if (e->length == 0)
5182 goto out;
5184 ret = fcache_get_fbuf (e, &fd, &the_fbuf, O_RDONLY, FBUF_READ|FBUF_SHARED);
5185 if (ret)
5186 goto out;
5188 ret = fdir_dirp(&the_fbuf);
5190 fbuf_end (&the_fbuf);
5191 close (fd);
5193 if (e->status.FileType == TYPE_DIR)
5194 assert(ret);
5195 else
5196 assert(!ret);
5198 out:
5199 ReleaseWriteLock (&e->lock);
5201 return FALSE;
5205 * Verifies that directories seems to be directories and files doesn't
5206 * seems to be directories, note that false positives are
5207 * possible in the latter of these cases, so this should not be turned on
5208 * default.
5211 void
5212 fcache_check_dirs(void)
5214 listiter (lrulist, check_dir, NULL);