use a cell that actually exists
[arla.git] / arlad / inter.c
blob63bb61bd4add6d7da9305b5d322ea97d80ef3821
1 /*
2 * Copyright (c) 1995 - 2002, 2005 - 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 * Interface to the cache manager.
38 #include "arla_local.h"
39 RCSID("$Id$") ;
41 #include <nnpfs/nnpfs_message.h>
43 #ifdef BLOCKS_PARANOIA
44 Bool cm_consistencyp = TRUE;
45 #else
46 Bool cm_consistencyp = FALSE;
47 #endif
50 * Return the rights for user cred and entry e.
51 * If the rights are not existant fill in the entry.
52 * The locking of e is up to the caller.
55 static u_long
56 getrights (FCacheEntry *e, CredCacheEntry *ce)
58 AccessEntry *ae;
59 int error;
61 while (findaccess (ce->cred, e->acccache, &ae) == FALSE) {
62 if ((error = read_attr(e, ce)) != 0)
63 return 0; /* XXXX we want to return errno */
65 return ae->access;
69 * Check to see if the operation(s) mask are allowed to user cred on
70 * file e.
72 * Return 0 on success, else a suitable error code.
75 int
76 cm_checkright (FCacheEntry *e, u_long mask, CredCacheEntry *ce)
78 uint32_t modify = AWRITE | AINSERT | ADELETE | ALOCK | AADMIN;
79 long voltype = getvoltype(e->fid.fid.Volume, e->volume);
80 u_long rights;
82 /* We won't be able to modify readonly volumes */
83 if (voltype != RWVOL && (mask & modify) != 0)
84 return EROFS;
86 if (e->status.FileType == TYPE_LINK &&
87 e->anonaccess & ALIST)
88 return 0;
90 if ((e->anonaccess & mask) == mask)
91 return 0;
93 rights = getrights (e, ce);
94 if (e->status.FileType != TYPE_DIR && (rights & AADMIN))
95 rights |= AREAD | AWRITE;
97 if (e->status.FileType == TYPE_LINK &&
98 rights & ALIST)
99 return 0;
101 if ((rights & mask) == mask)
102 return 0;
104 return EACCES;
108 * Check whether to modify cache or not. If we have stale data, get
109 * fresh data and return FALSE, else return TRUE to indicate that
110 * cache modification is the way to go.
113 static Bool
114 should_modify_locally(FCacheEntry **e, CredCacheEntry **ce, int *error)
116 if (block_any(*e) != BLOCK_NONE)
117 return TRUE;
119 *error = fcache_get_data(e, ce, 0,
120 fcache_get_status_length(&(*e)->status));
121 return FALSE;
124 static int log_fd;
125 static FILE *log_fp;
131 void
132 cm_init (void)
134 log_fd = open ("log", O_WRONLY | O_APPEND | O_CREAT | O_BINARY, 0666);
135 if (log_fd < 0)
136 arla_err (1, ADEBERROR, errno, "open log");
137 log_fp = fdopen (log_fd, "a");
138 if (log_fp == NULL)
139 arla_err (1, ADEBERROR, errno, "fdopen");
146 void
147 cm_store_state (void)
149 fclose (log_fp);
156 static void
157 log_operation (const char *fmt, ...)
159 va_list args;
160 struct timeval now;
162 if(connected_mode == CONNECTED && cm_consistencyp == FALSE)
163 return;
165 va_start (args, fmt);
166 gettimeofday (&now, NULL);
167 fprintf (log_fp, "%lu.%lu ",
168 (unsigned long)now.tv_sec,
169 (unsigned long)now.tv_usec);
170 vfprintf (log_fp, fmt, args);
171 va_end (args);
179 void
180 cm_turn_on_consistency_check(void)
182 cm_consistencyp = TRUE;
186 * Check consistency of the fcache.
187 * Will break the log-file.
190 void
191 cm_check_consistency (void)
193 static unsigned int log_times = 0;
194 static unsigned int file_times = 0;
195 int64_t calc_size, real_size;
196 char newname[MAXPATHLEN];
198 if (cm_consistencyp == FALSE)
199 return;
201 fcache_check_dirs();
202 fcache_check_blocks();
204 calc_size = fcache_calculate_usage();
205 real_size = fcache_usedbytes ();
207 if (calc_size != real_size) {
208 log_operation ("consistency check not guaranteed "
209 "(calc: %d, real: %d, diff %d), aborting\n",
210 (int) calc_size, (int) real_size,
211 (int)(calc_size - real_size));
212 cm_store_state ();
213 abort();
215 if (log_times % 100000 == 0) {
216 log_operation ("consistency check ok, rotating logs\n");
217 cm_store_state ();
218 snprintf (newname, sizeof(newname), "log.%d", file_times++);
219 rename ("log", newname);
220 cm_init ();
221 log_operation ("brave new world\n");
223 log_times++;
227 * These functions often take a FID as an argument to be general, but
228 * they are intended to be called from a vnode-type of layer.
232 * The interface to the open-routine.
236 cm_open (FCacheEntry *entry, CredCacheEntry *ce, u_int tokens)
238 u_long mask;
239 int error = 0;
241 switch(tokens) {
242 case NNPFS_DATA_R:
243 mask = AREAD;
244 break;
245 case NNPFS_OPEN_NR:
246 #if 0
247 case NNPFS_OPEN_SR:
248 #endif
249 mask = AREAD;
250 tokens |= NNPFS_DATA_R;
251 break;
252 case NNPFS_DATA_W:
253 mask = AWRITE;
254 break;
255 case NNPFS_OPEN_NW:
256 mask = AREAD | AWRITE;
257 tokens |= NNPFS_DATA_R | NNPFS_DATA_W | NNPFS_OPEN_NR;
258 break;
259 default:
260 arla_warnx (ADEBCM, "cm_open(): unknown token: %d, assuming AREAD",
261 tokens);
262 mask = AREAD;
263 tokens |= NNPFS_DATA_R;
264 #if 1
265 assert(FALSE);
266 #endif
269 error = cm_checkright(entry, mask, ce);
270 if (!error) {
271 assert(entry->flags.attrusedp);
272 entry->flags.datausedp = TRUE;
273 entry->tokens |= tokens;
275 log_operation ("open (%ld,%lu,%lu,%lu) %u\n",
276 entry->fid.Cell,
277 entry->fid.fid.Volume,
278 entry->fid.fid.Vnode,
279 entry->fid.fid.Unique,
280 mask);
283 cm_check_consistency();
285 return error;
289 * write ("close"). Set flags and if we opened the file for writing,
290 * write it back to the server.
294 cm_write(FCacheEntry *entry, int flag, uint64_t offset, uint64_t length,
295 AFSStoreStatus *status, CredCacheEntry* ce)
297 int error = 0;
299 if (flag & NNPFS_WRITE) {
300 if (flag & NNPFS_FSYNC)
301 status->Mask |= SS_FSYNC;
303 error = write_data(entry, NULL, offset, length, status, ce);
305 if (error) {
306 arla_warn (ADEBCM, error, "writing back file");
307 return error;
311 log_operation ("write (%ld,%lu,%lu,%lu) %d\n",
312 entry->fid.Cell,
313 entry->fid.fid.Volume,
314 entry->fid.fid.Vnode,
315 entry->fid.fid.Unique,
316 flag);
318 cm_check_consistency();
320 return error;
324 * getattr - read the attributes from this file.
328 cm_getattr (FCacheEntry *entry,
329 CredCacheEntry *ce)
331 int error = 0;
333 arla_warnx (ADEBCM, "cm_getattr");
335 AssertExclLocked(&entry->lock);
337 error = fcache_verify_attr (entry, NULL, NULL, ce);
338 if (error)
339 return error;
341 arla_warnx (ADEBCM, "cm_getattr: done get attr");
343 error = cm_checkright(entry,
344 entry->status.FileType == TYPE_FILE ? AREAD : 0,
345 ce);
346 if (!error) {
347 entry->flags.attrusedp = TRUE;
348 fcache_node_setkernelp(entry, TRUE);
350 log_operation ("getattr (%ld,%lu,%lu,%lu)\n",
351 entry->fid.Cell,
352 entry->fid.fid.Volume,
353 entry->fid.fid.Vnode,
354 entry->fid.fid.Unique);
357 if (!entry->flags.datausedp)
358 entry->tokens &= ~(NNPFS_DATA_MASK | NNPFS_OPEN_MASK);
360 arla_warnx (ADEBCM, "cm_getattr: return: %d", error);
361 cm_check_consistency();
363 return error;
367 * setattr - set the attributes of this file. These are immediately
368 * sent to the FS.
372 cm_setattr (FCacheEntry *entry, AFSStoreStatus *attr, CredCacheEntry* ce)
374 int error = fcache_verify_attr (entry, NULL, NULL, ce);
375 if (error)
376 return error;
378 error = cm_checkright(entry, AWRITE, ce);
379 if (!error) {
380 arla_warnx (ADEBCM, "cm_setattr: Writing status");
381 error = write_attr (entry, attr, ce);
383 log_operation ("setattr (%ld,%lu,%lu,%lu)\n",
384 entry->fid.Cell,
385 entry->fid.fid.Volume,
386 entry->fid.fid.Vnode,
387 entry->fid.fid.Unique);
390 cm_check_consistency();
391 return error;
395 * ftruncate - make the specified file have a specified size
399 cm_ftruncate (FCacheEntry *entry, off_t size,
400 AFSStoreStatus *storestatus, CredCacheEntry* ce)
402 int error = 0;
404 error = fcache_verify_attr (entry, NULL, NULL, ce);
405 if (error)
406 return error;
408 error = cm_checkright(entry, AWRITE, ce);
409 if (!error) {
410 error = truncate_file (entry, size, storestatus, ce);
412 log_operation ("ftruncate (%ld,%lu,%lu,%lu) %lu\n",
413 entry->fid.Cell,
414 entry->fid.fid.Volume,
415 entry->fid.fid.Vnode,
416 entry->fid.fid.Unique,
417 (unsigned long)size);
420 cm_check_consistency();
421 return error;
425 * Expand `src' into `dest' (of size `dst_sz'), expanding `str' to
426 * `replacement'. Return number of characters written to `dest'
427 * (excluding terminating zero) or `dst_sz' if there's not enough
428 * room.
431 static int
432 expand_sys (char *dest, size_t dst_sz, const char *src,
433 const char *str, const char *rep)
435 char *destp = dest;
436 const char *srcp = src;
437 char *s;
438 int n = 0;
439 int len;
440 size_t str_len = strlen(str);
441 size_t rep_len = strlen(rep);
442 size_t src_len = strlen(src);
444 while ((s = strstr (srcp, str)) != NULL) {
445 len = s - srcp;
447 if (dst_sz <= n + len + rep_len)
448 return dst_sz;
450 memcpy (destp, srcp, len);
451 memcpy (destp + len, rep, rep_len);
452 n += len + rep_len;
453 destp += len + rep_len;
454 srcp = s + str_len;
456 len = src_len - (srcp - src);
457 if (dst_sz <= n + len)
458 return dst_sz;
459 memcpy (destp, srcp, len);
460 n += len;
461 destp[len] = '\0';
462 return n;
466 * Find this entry in the directory. If the entry happens to point to
467 * a mount point, then we follow that and return the root directory of
468 * the volume. Hopefully this is the only place where we need to think
469 * about mount points (which are followed iff follow_mount_point).
473 cm_lookup (FCacheEntry **entry,
474 const char *name,
475 VenusFid *res,
476 CredCacheEntry** ce,
477 int follow_mount_point)
479 char tmp_name[MAXPATHLEN];
480 int error = 0;
482 error = fcache_get_data(entry, ce, 0, 0);
483 if (error)
484 return error;
486 if (strstr (name, "@sys") != NULL) {
487 int i;
489 for (i = 0; i < sysnamenum; i++) {
490 int size = expand_sys (tmp_name, sizeof(tmp_name), name,
491 "@sys", sysnamelist[i]);
492 if (size >= sizeof(tmp_name))
493 continue;
494 error = adir_lookup (*entry, tmp_name, res);
495 if (error == 0)
496 break;
498 if (i == sysnamenum)
499 error = ENOENT;
501 } else
502 error = adir_lookup (*entry, name, res);
504 if (error)
505 return error;
509 * Or
512 if (strcmp(".", name) == 0) {
515 * If we are looking up "." we don't want to follow the
516 * mountpoint, do fcache_verify_attr to force resolving of
517 * fake mountpoints.
520 error = fcache_verify_attr (*entry, NULL, NULL, *ce);
521 if (error)
522 goto out;
524 *res = (*entry)->fid;
525 } else if (strcmp("..", name) == 0) {
528 * First make sure we don't following mountpoints for ".."
529 * First, We are sure, its not a mountpoint. Second since
530 * following mountpoints lock both parent and child, and
531 * mountpoints breaks the tree that usully filesystem enforce
532 * (non-directed graph) we can deadlock one thread looks up
533 * from "root" -> "directory" and a second from "directory"
534 * -> "..".
538 * The ".." at the top of a volume just points to the volume
539 * root itself, so try to get the real ".." from the volume
540 * cache instead.
543 if (VenusFid_cmp(&(*entry)->fid, res) == 0) {
544 long voltype;
546 error = fcache_verify_attr (*entry, NULL, NULL, *ce);
547 if (error)
548 goto out;
550 voltype = getvoltype((*entry)->fid.fid.Volume, (*entry)->volume);
551 *res = (*entry)->volume->parent[voltype].fid; /* entry->parent */
554 } else if (follow_mount_point) {
555 error = followmountpoint (res, &(*entry)->fid, *entry, ce);
556 if (error)
557 goto out;
559 out:
560 log_operation ("lookup (%ld,%lu,%lu,%lu) %s\n",
561 (*entry)->fid.Cell,
562 (*entry)->fid.fid.Volume,
563 (*entry)->fid.fid.Vnode,
564 (*entry)->fid.fid.Unique,
565 name);
567 cm_check_consistency();
568 return error;
572 * Create this file and more.
576 cm_create (FCacheEntry **dir, const char *name, AFSStoreStatus *store_attr,
577 FCacheEntry **res, CredCacheEntry **ce)
579 int error = 0;
581 error = fcache_get_data (dir, ce, 0, 0);
582 if (error)
583 return error;
585 error = cm_checkright(*dir, AINSERT, *ce);
586 if (!error) {
587 error = create_file (*dir, name, store_attr, res, *ce);
588 if (error == 0 && should_modify_locally(dir, ce, &error))
589 error = adir_creat (*dir, name, (*res)->fid.fid);
592 log_operation ("create (%ld,%lu,%lu,%lu) %s\n",
593 (*dir)->fid.Cell,
594 (*dir)->fid.fid.Volume,
595 (*dir)->fid.fid.Vnode,
596 (*dir)->fid.fid.Unique,
597 name);
599 cm_check_consistency();
600 return error;
604 * Create a new directory
608 cm_mkdir (FCacheEntry **dir, const char *name,
609 AFSStoreStatus *store_attr,
610 VenusFid *res, AFSFetchStatus *fetch_attr,
611 CredCacheEntry **ce)
613 int error = 0;
615 error = fcache_get_data (dir, ce, 0, 0);
616 if (error)
617 return error;
619 error = cm_checkright(*dir, AINSERT, *ce);
620 if (!error) {
621 error = create_directory (*dir, name, store_attr,
622 res, fetch_attr, *ce);
623 if (error == 0 && should_modify_locally(dir, ce, &error))
624 error = adir_creat (*dir, name, res->fid);
628 log_operation ("mkdir (%ld,%lu,%lu,%lu) %s\n",
629 (*dir)->fid.Cell,
630 (*dir)->fid.fid.Volume,
631 (*dir)->fid.fid.Vnode,
632 (*dir)->fid.fid.Unique,
633 name);
635 cm_check_consistency();
636 return error;
640 * Create a symlink
642 * If realfid is non-NULL, we mark the symlink with kernelp flag and
643 * return its fid in realfid.
648 cm_symlink (FCacheEntry **dir,
649 const char *name, AFSStoreStatus *store_attr,
650 VenusFid *res, VenusFid *realfid,
651 AFSFetchStatus *fetch_attr,
652 const char *contents,
653 CredCacheEntry **ce)
655 FCacheEntry *symlink_entry;
656 int error = 0;
658 error = fcache_get_data (dir, ce, 0, 0);
659 if (error)
660 return error;
662 error = cm_checkright(*dir, AINSERT, *ce);
663 if (error)
664 return error;
666 /* It seems Transarc insists on mount points having mode bits 0644 */
668 if (contents[0] == '%' || contents[0] == '#') {
669 store_attr->UnixModeBits = 0644;
670 store_attr->Mask |= SS_MODEBITS;
671 } else if (store_attr->Mask & SS_MODEBITS
672 && store_attr->UnixModeBits == 0644)
673 store_attr->UnixModeBits = 0755;
675 error = create_symlink (*dir, name, store_attr,
676 res, fetch_attr,
677 contents, *ce);
679 if (error == 0 && should_modify_locally(dir, ce, &error))
680 error = adir_creat (*dir, name, res->fid);
682 if (error)
683 goto out;
685 error = followmountpoint(res, &(*dir)->fid, NULL, ce);
686 if (error)
687 goto out;
690 * If the new symlink is a mountpoint and it points
691 * to dir_fid we will deadlock if we look it up.
694 if (realfid == NULL) {
696 * Caller doesn't care, don't bother.
697 * ...and don't mark the symlink with kernelp
699 } else if (VenusFid_cmp (res, &(*dir)->fid) != 0) {
701 error = fcache_get (&symlink_entry, *res, *ce);
702 if (error)
703 goto out;
705 error = fcache_verify_attr (symlink_entry, *dir, name, *ce);
706 if (error) {
707 fcache_release (symlink_entry);
708 goto out;
711 fcache_node_setkernelp(symlink_entry, TRUE);
713 *fetch_attr = symlink_entry->status;
714 *realfid = *fcache_realfid (symlink_entry);
716 fcache_release (symlink_entry);
717 } else {
718 *fetch_attr = (*dir)->status;
719 *realfid = *fcache_realfid (*dir);
722 log_operation ("symlink (%ld,%lu,%lu,%lu) %s %s\n",
723 (*dir)->fid.Cell,
724 (*dir)->fid.fid.Volume,
725 (*dir)->fid.fid.Vnode,
726 (*dir)->fid.fid.Unique,
727 name,
728 contents);
730 out:
731 cm_check_consistency();
732 return error;
736 * Create a hard link.
740 cm_link (FCacheEntry **dir,
741 const char *name,
742 FCacheEntry *file,
743 CredCacheEntry **ce)
745 int error = 0;
747 error = fcache_get_data (dir, ce, 0, 0);
748 if (error)
749 return error;
751 error = fcache_verify_attr (file, *dir, NULL, *ce);
752 if (error)
753 goto out;
755 error = cm_checkright(*dir, AINSERT, *ce);
756 if (!error) {
757 error = create_link (*dir, name, file, *ce);
758 if (error == 0) {
759 if (should_modify_locally(dir, ce, &error))
760 error = adir_creat (*dir, name, file->fid.fid);
764 log_operation ("link (%ld,%lu,%lu,%lu) (%ld,%lu,%lu,%lu) %s\n",
765 (*dir)->fid.Cell,
766 (*dir)->fid.fid.Volume,
767 (*dir)->fid.fid.Vnode,
768 (*dir)->fid.fid.Unique,
769 file->fid.Cell,
770 file->fid.fid.Volume,
771 file->fid.fid.Vnode,
772 file->fid.fid.Unique,
773 name);
775 out:
776 cm_check_consistency();
777 return error;
781 * generic function for both remove and rmdir
784 static int
785 sub_remove(FCacheEntry **dir, const char *name, FCacheEntry **child,
786 CredCacheEntry **ce,
787 const char *operation,
788 int (*func)(FCacheEntry *dir,
789 const char *name,
790 FCacheEntry *child,
791 CredCacheEntry *ce))
793 int error = 0;
795 error = fcache_get_data (dir, ce, 0, 0);
796 if (error)
797 return error;
799 error = cm_checkright(*dir, ADELETE, *ce);
800 if (!error) {
801 error = (*func) (*dir, name, *child, *ce);
802 if (error == 0 && should_modify_locally(dir, ce, &error))
803 error = adir_remove (*dir, name);
806 log_operation ("%s (%ld,%lu,%lu,%lu) %s\n",
807 operation,
808 (*dir)->fid.Cell,
809 (*dir)->fid.fid.Volume,
810 (*dir)->fid.fid.Vnode,
811 (*dir)->fid.fid.Unique,
812 name);
814 cm_check_consistency();
815 return error;
819 * Remove the file named `name' in the directory `dir'.
823 cm_remove(FCacheEntry **dir, const char *name,
824 FCacheEntry **child, CredCacheEntry **ce)
826 return sub_remove(dir, name, child, ce, "remove", remove_file);
830 * Remove the directory named `name' in the directory `dir'.
834 cm_rmdir(FCacheEntry **dir, const char *name,
835 FCacheEntry **child, CredCacheEntry **ce)
837 return sub_remove(dir, name, child, ce, "rmdir", remove_directory);
841 * Read a symlink and null-terminate
844 static int
845 read_symlink(FCacheEntry *entry, char *buf, size_t bufsize)
847 int error;
848 int len;
849 fbuf f;
851 error = fcache_get_fbuf(entry, &f, FBUF_READ);
852 if (error) {
853 arla_warn (ADEBWARN, errno, "fcache_get_fbuf");
854 return error;
857 len = fbuf_len(&f);
858 if (len >= bufsize || len <= 0) {
859 abuf_end(&f);
860 arla_warnx(ADEBWARN, "symlink with bad length: %d", len);
861 return EIO;
864 memcpy(buf, fbuf_buf(&f), bufsize);
865 buf[len] = '\0';
867 abuf_end(&f);
869 return 0;
874 * Apple's Finder doesn't understand EXDEV within a "Volume", so we do
875 * a copy+remove.
877 * XXX verify hardlink handling and cross cell renames
880 #define RENAME_MAX_DEPTH 10
883 * Arguments struct for rename_remove_node().
886 typedef struct remove_node_args {
887 CredCacheEntry *ce;
888 FCacheEntry *dir;
889 int depth;
890 int error;
891 } remove_node_args;
894 * Remove a node or tree to given destination.
896 * We assume that relevant checks have been performed.
897 * Meant as a fdir_readdir_func.
900 static int
901 rename_remove_node(VenusFid *fid, const char *name, void *a)
903 remove_node_args *args = (remove_node_args *)a;
904 CredCacheEntry *ce = args->ce;
905 FCacheEntry *entry;
906 uint64_t len;
907 int error;
909 if (strcmp(".", name) == 0 || strcmp("..", name) == 0)
910 return 0;
912 error = fcache_get(&entry, *fid, ce);
913 if (!error)
914 error = fcache_verify_attr(entry, NULL, NULL, ce);
916 if (error)
917 return error;
919 len = fcache_get_status_length(&args->dir->status);
921 if (entry->status.FileType == TYPE_DIR) {
922 remove_node_args args2 = *args;
923 args2.dir = entry;
924 args2.depth++;
926 if (args2.depth >= RENAME_MAX_DEPTH)
927 error = EXDEV; /* restore original error */
928 else
929 error = adir_readdir(&entry,
930 rename_remove_node,
931 (void *)&args2,
932 &ce);
933 if (!error)
934 error = args2.error;
936 if (!error)
937 error = cm_rmdir(&args->dir, name, &entry, &ce);
939 fcache_release(entry);
940 } else {
941 error = cm_remove(&args->dir, name, &entry, &ce);
942 fcache_release(entry);
945 if (error)
946 args->error = error;
949 * When last entry is removed, dir may be truncated. If so, abort
950 * readdir so it doesn't try to read nonexistent directory pages.
952 * A more traditional approach would be
953 * 1) readdir -> name list
954 * 2) foreach(list) {unlink();}
956 if (fcache_get_status_length(&args->dir->status) < len)
957 return 1;
959 return error;
963 * Remove a node or tree to given destination, usable version.
966 static int
967 rename_remove_tree(FCacheEntry *dir, VenusFid *fid,
968 const char *name, CredCacheEntry *ce)
970 remove_node_args args;
972 args.ce = ce;
973 args.depth = 0;
974 args.error = 0;
975 args.dir = dir;
977 return rename_remove_node(fid, name, (void*)&args);
980 typedef struct rename_fid_pair {
981 VenusFid old;
982 VenusFid new;
983 } rename_fid_pair;
986 * Arguments struct for rename_copy_node().
989 typedef struct copy_node_args {
990 CredCacheEntry *ce;
991 CredCacheEntry *ce2;
992 FCacheEntry *target;
993 rename_fid_pair *hardlinks;
994 int nlinks;
995 int depth;
996 int error;
997 } copy_node_args;
999 /* Forward */
1000 static int rename_readdir_copy(VenusFid *fid, const char *name, void *a);
1003 * Copy a node or tree to given destination.
1005 * We assume that relevant checks have been performed and that
1006 * `old_entry' has valid data.
1009 static int
1010 rename_copy_node(FCacheEntry *old_entry, const char *name,
1011 copy_node_args *args)
1013 AFSFetchStatus fetch_attr;
1014 CredCacheEntry *ce = args->ce;
1015 CredCacheEntry *ce2 = args->ce2;
1016 AFSStoreStatus status;
1017 FCacheEntry *new_entry = NULL;
1018 VenusFid new_fid;
1019 int error = 0;
1021 arla_warnx(ADEBCM, "rename_copy_node(%s)", name);
1023 afsstatus2afsstorestatus(&old_entry->status, &status);
1025 if (old_entry->status.FileType == TYPE_DIR) {
1026 error = cm_mkdir(&args->target, name, &status, &new_fid,
1027 &fetch_attr, &ce2);
1028 if (!error)
1029 error = fcache_get(&new_entry, new_fid, ce2);
1031 if (!error) {
1032 copy_node_args args2 = *args;
1033 args2.target = new_entry;
1034 args2.hardlinks = NULL;
1035 args2.nlinks = 0;
1036 args2.depth++;
1038 if (args2.depth >= RENAME_MAX_DEPTH)
1039 error = EXDEV; /* restore original error */
1040 else
1041 error = adir_readdir(&old_entry, rename_readdir_copy,
1042 (void *)&args2, &ce);
1043 if (args2.nlinks)
1044 free(args2.hardlinks);
1046 if (!error)
1047 error = args2.error;
1049 } else if (old_entry->status.FileType == TYPE_FILE) {
1050 int linkp = 0;
1051 int createp = 1;
1053 if (old_entry->status.LinkCount > 1) {
1054 int i;
1056 linkp = 1;
1058 for (i = 0; i < args->nlinks; i++)
1059 if (VenusFid_cmp(&args->hardlinks[i].old, &old_entry->fid) == 0)
1060 break;
1062 if (i < args->nlinks) {
1063 /* we've already copied this fid */
1065 error = fcache_get(&new_entry, args->hardlinks[i].new, ce2);
1066 if (!error) {
1067 /* XXX hope it's still the same one */
1068 error = cm_link(&args->target, name,
1069 new_entry,
1070 &ce2);
1072 createp = 0;
1076 if (!error && createp)
1077 error = cm_create(&args->target, name, &status,
1078 &new_entry, &ce2);
1080 /* copy file data */
1081 if (!error)
1082 error = write_data(new_entry, old_entry,
1083 0, fcache_get_status_length(&old_entry->status),
1084 &status, ce2);
1086 if (linkp && createp && !error) {
1087 /* hard linked, add to list and copy the data */
1088 int n = args->nlinks + 1;
1089 if (n < 0) {
1090 error = EXDEV;
1091 } else {
1092 rename_fid_pair *tmp;
1093 tmp = realloc(args->hardlinks, n * sizeof(rename_fid_pair));
1094 if (tmp) {
1095 tmp[args->nlinks].old = old_entry->fid;
1096 tmp[args->nlinks].new = new_entry->fid;
1097 args->hardlinks = tmp;
1098 args->nlinks = n;
1099 } else {
1100 error = errno;
1104 } else if (old_entry->status.FileType == TYPE_LINK) {
1105 char buf[MAXPATHLEN];
1106 error = read_symlink(old_entry, buf, sizeof(buf));
1107 if (!error) {
1108 error = cm_symlink(&args->target, name,
1109 &status, &new_fid,
1110 NULL, &fetch_attr,
1111 buf, &ce2);
1113 } else {
1114 arla_warnx(ADEBWARN, "rename_copy_node: bad node type");
1117 if (new_entry)
1118 fcache_release(new_entry);
1120 return error;
1124 * Copy a node or tree to given destination.
1126 * fdir_readdir_func version.
1129 static int
1130 rename_readdir_copy(VenusFid *fid, const char *name, void *a)
1132 copy_node_args *args = (copy_node_args *)a;
1133 FCacheEntry *old_entry;
1134 int error;
1136 if (strcmp(".", name) == 0 || strcmp("..", name) == 0)
1137 return 0;
1139 error = fcache_get(&old_entry, *fid, args->ce);
1140 if (error)
1141 return error;
1143 error = fcache_get_data(&old_entry, &args->ce, 0, 0);
1144 if (!error) {
1145 error = rename_copy_node(old_entry, name, args);
1146 if (error)
1147 args->error = error;
1150 fcache_release(old_entry);
1152 return error;
1156 * Copy a node or tree to given destination, usable version.
1158 * Assumes that `old_entry' has valid data.
1161 static int
1162 rename_copy_tree(FCacheEntry *old_entry, FCacheEntry *target,
1163 const char *new_name, CredCacheEntry *ce, CredCacheEntry *ce2)
1165 copy_node_args args;
1166 int ret;
1168 args.ce = ce;
1169 args.ce2 = ce2;
1170 args.depth = 0;
1171 args.error = 0;
1172 args.target = target;
1173 args.hardlinks = NULL;
1174 args.nlinks = 0;
1176 ret = rename_copy_node(old_entry, new_name, &args);
1178 if (args.nlinks)
1179 free(args.hardlinks);
1181 return ret;
1185 * Arguments struct for rename_source_access().
1188 typedef struct source_access_args {
1189 CredCacheEntry *ce;
1190 int depth;
1191 int error;
1192 } source_access_args;
1195 * Check access rights for source node and recurse.
1197 * Meant as a fdir_readdir_func.
1200 static int
1201 rename_source_access(VenusFid *fid, const char *name, void *a)
1203 source_access_args *args = (source_access_args *)a;
1204 CredCacheEntry *ce = args->ce;
1205 FCacheEntry *entry;
1206 int error;
1208 if (strcmp(".", name) == 0 || strcmp("..", name) == 0)
1209 return 0;
1211 error = fcache_get(&entry, *fid, ce);
1212 if (!error)
1213 error = fcache_verify_attr (entry, NULL, NULL, ce);
1215 if (!error) {
1216 if (entry->status.FileType == TYPE_DIR) {
1217 error = cm_checkright(entry, ADELETE|AREAD, ce);
1218 if (!error) {
1219 /* Let's recurse */
1220 args->depth++;
1222 if (args->depth >= RENAME_MAX_DEPTH)
1223 error = EXDEV; /* restore original error */
1224 else
1225 error = adir_readdir(&entry, rename_source_access,
1226 a, &ce);
1227 args->depth--;
1228 if (!error)
1229 error = args->error;
1231 } else if (entry->status.FileType != TYPE_FILE
1232 && entry->status.FileType != TYPE_LINK) {
1233 error = EACCES;
1236 fcache_release(entry);
1239 if (error)
1240 args->error = error;
1242 return error;
1246 * Do some checks to be reasonably sure the operation won't fail.
1248 * XXX check size vs quota.
1251 static int
1252 rename_check_tree(FCacheEntry *oldnode, CredCacheEntry *ce)
1254 source_access_args args;
1255 int error;
1257 args.error = 0;
1258 args.depth = 0;
1259 args.ce = ce;
1261 error = adir_readdir(&oldnode,
1262 rename_source_access,
1263 (void *)&args,
1264 &ce);
1265 if (!error)
1266 error = args.error;
1268 return error;
1271 static int
1272 copy_remove_entry(FCacheEntry *old_dir, const char *old_name,
1273 FCacheEntry *new_dir, const char *new_name,
1274 VenusFid *new_fid, CredCacheEntry *ce, CredCacheEntry *ce2)
1276 VenusFid existing_fid, old_fid;
1277 FCacheEntry *old_entry = NULL;
1278 FCacheEntry *existing_entry = NULL;
1280 int dirp = 0;
1281 int error;
1283 error = adir_lookup(new_dir, new_name, &existing_fid);
1284 if (!error) {
1285 error = fcache_get(&existing_entry, existing_fid, ce2);
1286 if (error)
1287 return error;
1290 error = adir_lookup(old_dir, old_name, &old_fid);
1291 if (error)
1292 return error;
1294 /* check permissions (and hope they don't change) */
1295 error = cm_checkright(old_dir, ADELETE|AREAD, ce);
1296 if (!error)
1297 error = cm_checkright(new_dir, AINSERT, ce2);
1298 if (existing_entry && !error)
1299 error = cm_checkright(new_dir, ADELETE, ce2);
1300 if (error)
1301 return error;
1303 error = fcache_get(&old_entry, old_fid, ce);
1304 if (error)
1305 return error;
1307 if (old_entry->status.FileType != TYPE_FILE
1308 && old_entry->status.FileType != TYPE_DIR
1309 && old_entry->status.FileType != TYPE_LINK) {
1310 fcache_release(old_entry);
1311 return EXDEV;
1314 if (old_entry->status.FileType == TYPE_DIR)
1315 dirp = 1;
1317 error = fcache_get_data(&old_entry, &ce, 0, 0);
1319 if (dirp && !error) {
1320 if (!adir_emptyp(&old_entry, &ce))
1321 error = rename_check_tree(old_entry, ce);
1324 if (!error && existing_entry) {
1325 /* 1. we might be able to do a truncate+write. but not today */
1326 /* 2. rmdir fails if not empty. that's correct and well. */
1328 if (dirp)
1329 error = cm_rmdir(&new_dir, new_name, &existing_entry, &ce2);
1330 else
1331 error = cm_remove(&new_dir, new_name, &existing_entry, &ce2);
1334 if (!error) {
1335 error = rename_copy_tree(old_entry, new_dir, new_name, ce, ce2);
1337 if (error) {
1339 * Roll back changes as best we can. Unfortunately, existing
1340 * targets are already removed and permanently lost.
1343 VenusFid created_fid;
1344 int error2 = adir_lookup(new_dir, new_name, &created_fid);
1345 if (!error2)
1346 rename_remove_tree(new_dir, &created_fid, new_name, ce2);
1350 fcache_release(old_entry);
1352 if (!error)
1353 error = rename_remove_tree(old_dir, &old_fid, old_name, ce);
1355 return error;
1359 * Called when the object is being moved to a new directory, to be
1360 * able to update .. when required.
1363 static int
1364 potential_update_dir(FCacheEntry *child_entry,
1365 const VenusFid *new_parent_fid,
1366 FCacheEntry *parent_entry,
1367 int *update_child,
1368 CredCacheEntry **ce)
1370 int error;
1372 error = fcache_verify_attr (child_entry, parent_entry, NULL, *ce);
1373 if (error)
1374 return error;
1377 * if we're moving a directory.
1380 if (child_entry->status.FileType == TYPE_DIR) {
1381 fbuf the_fbuf;
1383 error = fcache_get_data(&child_entry, ce, 0, 0); /* XXX - check fake_mp */
1384 if (error)
1385 return error;
1387 error = fcache_get_fbuf(child_entry, &the_fbuf, FBUF_READ|FBUF_WRITE);
1388 if (error)
1389 return error;
1391 error = fdir_changefid (&the_fbuf, "..", new_parent_fid);
1392 abuf_end(&the_fbuf);
1393 if (error)
1394 return error;
1396 *update_child = 1;
1398 return 0;
1402 * Rename (old_parent_fid, old_name) -> (new_parent_fid, new_name)
1403 * update the `child' in the new directory if update_child.
1404 * set child_fid to the fid of the moved object.
1408 cm_rename(FCacheEntry **old_dir, const char *old_name,
1409 FCacheEntry **new_dir, const char *new_name,
1410 VenusFid *child_fid,
1411 int *update_child,
1412 CredCacheEntry **ce, CredCacheEntry **ce2)
1414 int error = 0;
1415 VenusFid new_fid, old_fid;
1416 Bool modify_old, modify_new;
1418 *update_child = 0;
1420 /* old parent dir */
1422 error = fcache_get_data (old_dir, ce, 0, 0);
1423 if (error)
1424 return error;
1426 /* new parent dir */
1428 error = fcache_get_data (new_dir, ce2, 0, 0);
1429 if (error)
1430 return error;
1432 error = cm_checkright(*old_dir, ADELETE, *ce);
1433 if (!error)
1434 error = cm_checkright(*new_dir, AINSERT, *ce2);
1435 if (error)
1436 goto out;
1438 error = rename_file (*old_dir, old_name, *new_dir, new_name, *ce);
1439 if (error == EXDEV) {
1441 * copy_remove_entry() only does normal operations (mkdir,
1442 * remove, etc) and thus gets correct parent/child fids, so we
1443 * can leave update_child unchanged (zero).
1445 error = copy_remove_entry(*old_dir, old_name, *new_dir,
1446 new_name, child_fid, *ce, *ce2);
1447 goto out;
1450 if (error)
1451 goto out;
1453 modify_old = should_modify_locally(old_dir, ce, &error);
1454 if (error)
1455 goto out;
1457 modify_new = should_modify_locally(new_dir, ce2, &error);
1458 if (error)
1459 goto out;
1462 * Lookup the old name (to get the fid of the new name)
1465 error = adir_lookup (*old_dir, old_name, &new_fid);
1467 if (error)
1468 goto out;
1470 *child_fid = new_fid;
1472 if (VenusFid_cmp (&(*old_dir)->fid, &(*new_dir)->fid)) {
1473 FCacheEntry *child_entry;
1475 error = fcache_get (&child_entry, *child_fid, *ce2);
1476 if (error)
1477 goto out;
1479 child_entry->parent = (*new_dir)->fid;
1481 error = potential_update_dir (child_entry, &(*new_dir)->fid,
1482 *new_dir, update_child, ce2);
1483 fcache_release (child_entry);
1484 if (error)
1485 goto out;
1489 * Lookup the new name, if it exists we need to silly
1490 * rename it was just killed on the fileserver.
1491 * XXXDISCO remember mark this node as dead
1494 error = adir_lookup (*new_dir, new_name, &old_fid);
1495 if (error == 0) {
1496 FCacheEntry *old_entry = fcache_find(old_fid);
1497 if (old_entry) {
1498 old_entry->flags.silly = TRUE;
1499 fcache_release (old_entry);
1501 if (modify_new)
1502 adir_remove (*new_dir, new_name);
1506 error = 0;
1509 * Now do the rename, ie create the new name and remove
1510 * the old name.
1513 if (modify_new)
1514 error = adir_creat (*new_dir, new_name, new_fid.fid);
1516 if (modify_old)
1517 error = adir_remove (*old_dir, old_name);
1519 out:
1520 if (!error)
1521 log_operation ("rename (%ld,%lu,%lu,%lu) (%ld,%lu,%lu,%lu) %s %s\n",
1522 (*old_dir)->fid.Cell,
1523 (*old_dir)->fid.fid.Volume,
1524 (*old_dir)->fid.fid.Vnode,
1525 (*old_dir)->fid.fid.Unique,
1526 (*new_dir)->fid.Cell,
1527 (*new_dir)->fid.fid.Volume,
1528 (*new_dir)->fid.fid.Vnode,
1529 (*new_dir)->fid.fid.Unique,
1530 old_name, new_name);
1532 cm_check_consistency();
1533 return error;
1537 * An emulation of kernel lookup, convert (fid, name) into
1538 * (res). Strips away leading /afs, removes double slashes,
1539 * and resolves symlinks.
1540 * Return 0 for success, otherwise -1.
1544 cm_walk (VenusFid fid,
1545 const char *name,
1546 VenusFid *res)
1548 VenusFid cwd = fid;
1549 char *base;
1550 VenusFid file;
1551 FCacheEntry *entry;
1552 FCacheEntry *dentry;
1553 int error;
1554 char symlink[MAXPATHLEN];
1555 char store_name[MAXPATHLEN];
1556 char *fname;
1557 CredCacheEntry *ce;
1559 ce = cred_get (fid.Cell, getuid(), CRED_ANY);
1561 strlcpy(store_name, name, sizeof(store_name));
1562 fname = store_name;
1564 do {
1565 /* set things up so that fname points to the remainder of the path,
1566 * whereas base points to the whatever preceeds the first /
1568 base = fname;
1569 fname = strchr(fname, '/');
1570 if (fname) {
1571 /* deal with repeated adjacent / chars by eliminating the
1572 * duplicates.
1574 while (*fname == '/') {
1575 *fname = '\0';
1576 fname++;
1580 /* deal with absolute pathnames first. */
1581 if (*base == '\0') {
1582 error = getroot(&cwd, ce);
1583 if (error) {
1584 arla_warn(ADEBWARN, error, "getroot");
1585 cred_free(ce);
1586 return -1;
1589 if (fname) {
1590 if (strncmp("afs",fname,3) == 0) {
1591 fname += 3;
1593 continue;
1594 } else {
1595 break;
1598 error = fcache_get(&dentry, cwd, ce);
1599 if (error) {
1600 arla_warn (ADEBWARN, error, "fcache_get");
1601 cred_free(ce);
1602 return -1;
1604 error = cm_lookup (&dentry, base, &file, &ce, TRUE);
1605 fcache_release(dentry);
1606 if (error) {
1607 arla_warn (ADEBWARN, error, "lookup(%s)", base);
1608 cred_free(ce);
1609 return -1;
1612 error = fcache_get(&entry, file, ce);
1613 if (error) {
1614 arla_warn (ADEBWARN, error, "fcache_get");
1615 cred_free(ce);
1616 return -1;
1619 /* handle symlinks here */
1620 if (entry->status.FileType == TYPE_LINK) {
1621 error = fcache_get_data (&entry, &ce, 0, 0);
1622 if (error) {
1623 fcache_release(entry);
1624 arla_warn (ADEBWARN, error, "fcache_get_data");
1625 cred_free(ce);
1626 return -1;
1629 error = read_symlink(entry, symlink, sizeof(symlink));
1630 if (error) {
1631 fcache_release(entry);
1632 arla_warn(ADEBWARN, error, "read_symlink");
1633 cred_free(ce);
1634 return -1;
1637 /* if we're not at the end (i.e. fname is not null), take
1638 * the expansion of the symlink and append fname to it.
1640 if (fname != NULL) {
1641 strcat (symlink, "/");
1642 strcat (symlink, fname);
1644 strlcpy(store_name, symlink, sizeof(store_name));
1645 fname = store_name;
1646 } else {
1647 /* if not a symlink, just update cwd */
1648 cwd = entry->fid;
1650 fcache_release(entry);
1652 /* the *fname condition below deals with a trailing / in a
1653 * path-name */
1654 } while (fname != NULL && *fname);
1655 *res = cwd;
1656 cred_free(ce);
1657 return 0;