2 * Copyright (c) 1995 - 2002, 2005 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
35 * Interface to the cache manager.
38 #include "arla_local.h"
41 #include <nnpfs/nnpfs_message.h>
43 #ifdef BLOCKS_PARANOIA
44 Bool cm_consistencyp
= TRUE
;
46 Bool cm_consistencyp
= FALSE
;
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.
56 getrights (FCacheEntry
*e
, CredCacheEntry
*ce
)
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 */
69 * Check to see if the operation(s) mask are allowed to user cred on
72 * Return 0 on success, else a suitable error code.
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
);
82 /* We won't be able to modify readonly volumes */
83 if (voltype
!= RWVOL
&& (mask
& modify
) != 0)
86 if (e
->status
.FileType
== TYPE_LINK
&&
87 e
->anonaccess
& ALIST
)
90 if ((e
->anonaccess
& mask
) == mask
)
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
&&
101 if ((rights
& mask
) == mask
)
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.
114 should_modify_locally(FCacheEntry
**e
, CredCacheEntry
**ce
, int *error
)
116 if (block_any(*e
) != BLOCK_NONE
)
119 *error
= fcache_get_data(e
, ce
, 0,
120 fcache_get_status_length(&(*e
)->status
));
134 log_fd
= open ("log", O_WRONLY
| O_APPEND
| O_CREAT
| O_BINARY
, 0666);
136 arla_err (1, ADEBERROR
, errno
, "open log");
137 log_fp
= fdopen (log_fd
, "a");
139 arla_err (1, ADEBERROR
, errno
, "fdopen");
147 cm_store_state (void)
157 log_operation (const char *fmt
, ...)
162 if(connected_mode
== CONNECTED
&& cm_consistencyp
== FALSE
)
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
);
180 cm_turn_on_consistency_check(void)
182 cm_consistencyp
= TRUE
;
186 * Check consistency of the fcache.
187 * Will break the log-file.
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
)
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
));
215 if (log_times
% 100000 == 0) {
216 log_operation ("consistency check ok, rotating logs\n");
218 snprintf (newname
, sizeof(newname
), "log.%d", file_times
++);
219 rename ("log", newname
);
221 log_operation ("brave new world\n");
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
)
250 tokens
|= NNPFS_DATA_R
;
256 mask
= AREAD
| AWRITE
;
257 tokens
|= NNPFS_DATA_R
| NNPFS_DATA_W
| NNPFS_OPEN_NR
;
260 arla_warnx (ADEBCM
, "cm_open(): unknown token: %d, assuming AREAD",
263 tokens
|= NNPFS_DATA_R
;
269 error
= cm_checkright(entry
, mask
, ce
);
271 assert(entry
->flags
.attrusedp
);
272 entry
->flags
.datausedp
= TRUE
;
273 entry
->tokens
|= tokens
;
275 log_operation ("open (%ld,%lu,%lu,%lu) %u\n",
277 entry
->fid
.fid
.Volume
,
278 entry
->fid
.fid
.Vnode
,
279 entry
->fid
.fid
.Unique
,
283 cm_check_consistency();
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
)
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
);
306 arla_warn (ADEBCM
, error
, "writing back file");
311 log_operation ("write (%ld,%lu,%lu,%lu) %d\n",
313 entry
->fid
.fid
.Volume
,
314 entry
->fid
.fid
.Vnode
,
315 entry
->fid
.fid
.Unique
,
318 cm_check_consistency();
324 * getattr - read the attributes from this file.
328 cm_getattr (FCacheEntry
*entry
,
333 arla_warnx (ADEBCM
, "cm_getattr");
335 AssertExclLocked(&entry
->lock
);
337 error
= fcache_verify_attr (entry
, NULL
, NULL
, ce
);
341 arla_warnx (ADEBCM
, "cm_getattr: done get attr");
343 error
= cm_checkright(entry
,
344 entry
->status
.FileType
== TYPE_FILE
? AREAD
: 0,
347 entry
->flags
.attrusedp
= TRUE
;
348 fcache_node_setkernelp(entry
, TRUE
);
350 log_operation ("getattr (%ld,%lu,%lu,%lu)\n",
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();
367 * setattr - set the attributes of this file. These are immediately
372 cm_setattr (FCacheEntry
*entry
, AFSStoreStatus
*attr
, CredCacheEntry
* ce
)
374 int error
= fcache_verify_attr (entry
, NULL
, NULL
, ce
);
378 error
= cm_checkright(entry
, AWRITE
, ce
);
380 arla_warnx (ADEBCM
, "cm_setattr: Writing status");
381 error
= write_attr (entry
, attr
, ce
);
383 log_operation ("setattr (%ld,%lu,%lu,%lu)\n",
385 entry
->fid
.fid
.Volume
,
386 entry
->fid
.fid
.Vnode
,
387 entry
->fid
.fid
.Unique
);
390 cm_check_consistency();
395 * ftruncate - make the specified file have a specified size
399 cm_ftruncate (FCacheEntry
*entry
, off_t size
,
400 AFSStoreStatus
*storestatus
, CredCacheEntry
* ce
)
404 error
= fcache_verify_attr (entry
, NULL
, NULL
, ce
);
408 error
= cm_checkright(entry
, AWRITE
, ce
);
410 error
= truncate_file (entry
, size
, storestatus
, ce
);
412 log_operation ("ftruncate (%ld,%lu,%lu,%lu) %lu\n",
414 entry
->fid
.fid
.Volume
,
415 entry
->fid
.fid
.Vnode
,
416 entry
->fid
.fid
.Unique
,
417 (unsigned long)size
);
420 cm_check_consistency();
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
432 expand_sys (char *dest
, size_t dst_sz
, const char *src
,
433 const char *str
, const char *rep
)
436 const char *srcp
= src
;
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
) {
447 if (dst_sz
<= n
+ len
+ rep_len
)
450 memcpy (destp
, srcp
, len
);
451 memcpy (destp
+ len
, rep
, rep_len
);
453 destp
+= len
+ rep_len
;
456 len
= src_len
- (srcp
- src
);
457 if (dst_sz
<= n
+ len
)
459 memcpy (destp
, srcp
, len
);
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
,
477 int follow_mount_point
)
479 char tmp_name
[MAXPATHLEN
];
482 error
= fcache_get_data(entry
, ce
, 0, 0);
486 if (strstr (name
, "@sys") != NULL
) {
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
))
494 error
= adir_lookup (*entry
, tmp_name
, res
);
502 error
= adir_lookup (*entry
, name
, res
);
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
520 error
= fcache_verify_attr (*entry
, NULL
, NULL
, *ce
);
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"
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
543 if (VenusFid_cmp(&(*entry
)->fid
, res
) == 0) {
546 error
= fcache_verify_attr (*entry
, NULL
, NULL
, *ce
);
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
);
560 log_operation ("lookup (%ld,%lu,%lu,%lu) %s\n",
562 (*entry
)->fid
.fid
.Volume
,
563 (*entry
)->fid
.fid
.Vnode
,
564 (*entry
)->fid
.fid
.Unique
,
567 cm_check_consistency();
572 * Create this file and more.
576 cm_create (FCacheEntry
**dir
, const char *name
, AFSStoreStatus
*store_attr
,
577 FCacheEntry
**res
, CredCacheEntry
**ce
)
581 error
= fcache_get_data (dir
, ce
, 0, 0);
585 error
= cm_checkright(*dir
, AINSERT
, *ce
);
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",
594 (*dir
)->fid
.fid
.Volume
,
595 (*dir
)->fid
.fid
.Vnode
,
596 (*dir
)->fid
.fid
.Unique
,
599 cm_check_consistency();
604 * Create a new directory
608 cm_mkdir (FCacheEntry
**dir
, const char *name
,
609 AFSStoreStatus
*store_attr
,
610 VenusFid
*res
, AFSFetchStatus
*fetch_attr
,
615 error
= fcache_get_data (dir
, ce
, 0, 0);
619 error
= cm_checkright(*dir
, AINSERT
, *ce
);
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",
630 (*dir
)->fid
.fid
.Volume
,
631 (*dir
)->fid
.fid
.Vnode
,
632 (*dir
)->fid
.fid
.Unique
,
635 cm_check_consistency();
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
,
655 FCacheEntry
*symlink_entry
;
658 error
= fcache_get_data (dir
, ce
, 0, 0);
662 error
= cm_checkright(*dir
, AINSERT
, *ce
);
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
,
679 if (error
== 0 && should_modify_locally(dir
, ce
, &error
))
680 error
= adir_creat (*dir
, name
, res
->fid
);
685 error
= followmountpoint(res
, &(*dir
)->fid
, NULL
, ce
);
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
);
705 error
= fcache_verify_attr (symlink_entry
, *dir
, name
, *ce
);
707 fcache_release (symlink_entry
);
711 fcache_node_setkernelp(symlink_entry
, TRUE
);
713 *fetch_attr
= symlink_entry
->status
;
714 *realfid
= *fcache_realfid (symlink_entry
);
716 fcache_release (symlink_entry
);
718 *fetch_attr
= (*dir
)->status
;
719 *realfid
= *fcache_realfid (*dir
);
722 log_operation ("symlink (%ld,%lu,%lu,%lu) %s %s\n",
724 (*dir
)->fid
.fid
.Volume
,
725 (*dir
)->fid
.fid
.Vnode
,
726 (*dir
)->fid
.fid
.Unique
,
731 cm_check_consistency();
736 * Create a hard link.
740 cm_link (FCacheEntry
**dir
,
747 error
= fcache_get_data (dir
, ce
, 0, 0);
751 error
= fcache_verify_attr (file
, *dir
, NULL
, *ce
);
755 error
= cm_checkright(*dir
, AINSERT
, *ce
);
757 error
= create_link (*dir
, name
, file
, *ce
);
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",
766 (*dir
)->fid
.fid
.Volume
,
767 (*dir
)->fid
.fid
.Vnode
,
768 (*dir
)->fid
.fid
.Unique
,
770 file
->fid
.fid
.Volume
,
772 file
->fid
.fid
.Unique
,
776 cm_check_consistency();
781 * generic function for both remove and rmdir
785 sub_remove(FCacheEntry
**dir
, const char *name
, FCacheEntry
**child
,
787 const char *operation
,
788 int (*func
)(FCacheEntry
*dir
,
795 error
= fcache_get_data (dir
, ce
, 0, 0);
799 error
= cm_checkright(*dir
, ADELETE
, *ce
);
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",
809 (*dir
)->fid
.fid
.Volume
,
810 (*dir
)->fid
.fid
.Vnode
,
811 (*dir
)->fid
.fid
.Unique
,
814 cm_check_consistency();
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
845 read_symlink(FCacheEntry
*entry
, char *buf
, size_t bufsize
)
851 error
= fcache_get_fbuf(entry
, &f
, FBUF_READ
);
853 arla_warn (ADEBWARN
, errno
, "fcache_get_fbuf");
858 if (len
>= bufsize
|| len
<= 0) {
860 arla_warnx(ADEBWARN
, "symlink with bad length: %d", len
);
864 memcpy(buf
, fbuf_buf(&f
), bufsize
);
874 * Apple's Finder doesn't understand EXDEV within a "Volume", so we do
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
{
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.
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
;
909 if (strcmp(".", name
) == 0 || strcmp("..", name
) == 0)
912 error
= fcache_get(&entry
, *fid
, ce
);
914 error
= fcache_verify_attr(entry
, NULL
, NULL
, ce
);
919 len
= fcache_get_status_length(&args
->dir
->status
);
921 if (entry
->status
.FileType
== TYPE_DIR
) {
922 remove_node_args args2
= *args
;
926 if (args2
.depth
>= RENAME_MAX_DEPTH
)
927 error
= EXDEV
; /* restore original error */
929 error
= adir_readdir(&entry
,
937 error
= cm_rmdir(&args
->dir
, name
, &entry
, &ce
);
939 fcache_release(entry
);
941 error
= cm_remove(&args
->dir
, name
, &entry
, &ce
);
942 fcache_release(entry
);
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
)
963 * Remove a node or tree to given destination, usable version.
967 rename_remove_tree(FCacheEntry
*dir
, VenusFid
*fid
,
968 const char *name
, CredCacheEntry
*ce
)
970 remove_node_args args
;
977 return rename_remove_node(fid
, name
, (void*)&args
);
980 typedef struct rename_fid_pair
{
986 * Arguments struct for rename_copy_node().
989 typedef struct copy_node_args
{
993 rename_fid_pair
*hardlinks
;
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.
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
;
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
,
1029 error
= fcache_get(&new_entry
, new_fid
, ce2
);
1032 copy_node_args args2
= *args
;
1033 args2
.target
= new_entry
;
1034 args2
.hardlinks
= NULL
;
1038 if (args2
.depth
>= RENAME_MAX_DEPTH
)
1039 error
= EXDEV
; /* restore original error */
1041 error
= adir_readdir(&old_entry
, rename_readdir_copy
,
1042 (void *)&args2
, &ce
);
1044 free(args2
.hardlinks
);
1047 error
= args2
.error
;
1049 } else if (old_entry
->status
.FileType
== TYPE_FILE
) {
1053 if (old_entry
->status
.LinkCount
> 1) {
1058 for (i
= 0; i
< args
->nlinks
; i
++)
1059 if (VenusFid_cmp(&args
->hardlinks
[i
].old
, &old_entry
->fid
) == 0)
1062 if (i
< args
->nlinks
) {
1063 /* we've already copied this fid */
1065 error
= fcache_get(&new_entry
, args
->hardlinks
[i
].new, ce2
);
1067 /* XXX hope it's still the same one */
1068 error
= cm_link(&args
->target
, name
,
1076 if (!error
&& createp
)
1077 error
= cm_create(&args
->target
, name
, &status
,
1080 /* copy file data */
1082 error
= write_data(new_entry
, old_entry
,
1083 0, fcache_get_status_length(&old_entry
->status
),
1086 if (linkp
&& createp
&& !error
) {
1087 /* hard linked, add to list and copy the data */
1088 int n
= args
->nlinks
+ 1;
1092 rename_fid_pair
*tmp
;
1093 tmp
= realloc(args
->hardlinks
, n
* sizeof(rename_fid_pair
));
1095 tmp
[args
->nlinks
].old
= old_entry
->fid
;
1096 tmp
[args
->nlinks
].new = new_entry
->fid
;
1097 args
->hardlinks
= tmp
;
1104 } else if (old_entry
->status
.FileType
== TYPE_LINK
) {
1105 char buf
[MAXPATHLEN
];
1106 error
= read_symlink(old_entry
, buf
, sizeof(buf
));
1108 error
= cm_symlink(&args
->target
, name
,
1114 arla_warnx(ADEBWARN
, "rename_copy_node: bad node type");
1118 fcache_release(new_entry
);
1124 * Copy a node or tree to given destination.
1126 * fdir_readdir_func version.
1130 rename_readdir_copy(VenusFid
*fid
, const char *name
, void *a
)
1132 copy_node_args
*args
= (copy_node_args
*)a
;
1133 FCacheEntry
*old_entry
;
1136 if (strcmp(".", name
) == 0 || strcmp("..", name
) == 0)
1139 error
= fcache_get(&old_entry
, *fid
, args
->ce
);
1143 error
= fcache_get_data(&old_entry
, &args
->ce
, 0, 0);
1145 error
= rename_copy_node(old_entry
, name
, args
);
1147 args
->error
= error
;
1150 fcache_release(old_entry
);
1156 * Copy a node or tree to given destination, usable version.
1158 * Assumes that `old_entry' has valid data.
1162 rename_copy_tree(FCacheEntry
*old_entry
, FCacheEntry
*target
,
1163 const char *new_name
, CredCacheEntry
*ce
, CredCacheEntry
*ce2
)
1165 copy_node_args args
;
1172 args
.target
= target
;
1173 args
.hardlinks
= NULL
;
1176 ret
= rename_copy_node(old_entry
, new_name
, &args
);
1179 free(args
.hardlinks
);
1185 * Arguments struct for rename_source_access().
1188 typedef struct source_access_args
{
1192 } source_access_args
;
1195 * Check access rights for source node and recurse.
1197 * Meant as a fdir_readdir_func.
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
;
1208 if (strcmp(".", name
) == 0 || strcmp("..", name
) == 0)
1211 error
= fcache_get(&entry
, *fid
, ce
);
1213 error
= fcache_verify_attr (entry
, NULL
, NULL
, ce
);
1216 if (entry
->status
.FileType
== TYPE_DIR
) {
1217 error
= cm_checkright(entry
, ADELETE
|AREAD
, ce
);
1222 if (args
->depth
>= RENAME_MAX_DEPTH
)
1223 error
= EXDEV
; /* restore original error */
1225 error
= adir_readdir(&entry
, rename_source_access
,
1229 error
= args
->error
;
1231 } else if (entry
->status
.FileType
!= TYPE_FILE
1232 && entry
->status
.FileType
!= TYPE_LINK
) {
1236 fcache_release(entry
);
1240 args
->error
= error
;
1246 * Do some checks to be reasonably sure the operation won't fail.
1248 * XXX check size vs quota.
1252 rename_check_tree(FCacheEntry
*oldnode
, CredCacheEntry
*ce
)
1254 source_access_args args
;
1261 error
= adir_readdir(&oldnode
,
1262 rename_source_access
,
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
;
1283 error
= adir_lookup(new_dir
, new_name
, &existing_fid
);
1285 error
= fcache_get(&existing_entry
, existing_fid
, ce2
);
1290 error
= adir_lookup(old_dir
, old_name
, &old_fid
);
1294 /* check permissions (and hope they don't change) */
1295 error
= cm_checkright(old_dir
, ADELETE
|AREAD
, ce
);
1297 error
= cm_checkright(new_dir
, AINSERT
, ce2
);
1298 if (existing_entry
&& !error
)
1299 error
= cm_checkright(new_dir
, ADELETE
, ce2
);
1303 error
= fcache_get(&old_entry
, old_fid
, ce
);
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
);
1314 if (old_entry
->status
.FileType
== TYPE_DIR
)
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. */
1329 error
= cm_rmdir(&new_dir
, new_name
, &existing_entry
, &ce2
);
1331 error
= cm_remove(&new_dir
, new_name
, &existing_entry
, &ce2
);
1335 error
= rename_copy_tree(old_entry
, new_dir
, new_name
, ce
, ce2
);
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
);
1346 rename_remove_tree(new_dir
, &created_fid
, new_name
, ce2
);
1350 fcache_release(old_entry
);
1353 error
= rename_remove_tree(old_dir
, &old_fid
, old_name
, ce
);
1359 * Called when the object is being moved to a new directory, to be
1360 * able to update .. when required.
1364 potential_update_dir(FCacheEntry
*child_entry
,
1365 const VenusFid
*new_parent_fid
,
1366 FCacheEntry
*parent_entry
,
1368 CredCacheEntry
**ce
)
1372 error
= fcache_verify_attr (child_entry
, parent_entry
, NULL
, *ce
);
1377 * if we're moving a directory.
1380 if (child_entry
->status
.FileType
== TYPE_DIR
) {
1383 error
= fcache_get_data(&child_entry
, ce
, 0, 0); /* XXX - check fake_mp */
1387 error
= fcache_get_fbuf(child_entry
, &the_fbuf
, FBUF_READ
|FBUF_WRITE
);
1391 error
= fdir_changefid (&the_fbuf
, "..", new_parent_fid
);
1392 abuf_end(&the_fbuf
);
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
,
1412 CredCacheEntry
**ce
, CredCacheEntry
**ce2
)
1415 VenusFid new_fid
, old_fid
;
1416 Bool modify_old
, modify_new
;
1420 /* old parent dir */
1422 error
= fcache_get_data (old_dir
, ce
, 0, 0);
1426 /* new parent dir */
1428 error
= fcache_get_data (new_dir
, ce2
, 0, 0);
1432 error
= cm_checkright(*old_dir
, ADELETE
, *ce
);
1434 error
= cm_checkright(*new_dir
, AINSERT
, *ce2
);
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
);
1453 modify_old
= should_modify_locally(old_dir
, ce
, &error
);
1457 modify_new
= should_modify_locally(new_dir
, ce2
, &error
);
1462 * Lookup the old name (to get the fid of the new name)
1465 error
= adir_lookup (*old_dir
, old_name
, &new_fid
);
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
);
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
);
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
);
1496 FCacheEntry
*old_entry
= fcache_find(old_fid
);
1498 old_entry
->flags
.silly
= TRUE
;
1499 fcache_release (old_entry
);
1502 adir_remove (*new_dir
, new_name
);
1509 * Now do the rename, ie create the new name and remove
1514 error
= adir_creat (*new_dir
, new_name
, new_fid
.fid
);
1517 error
= adir_remove (*old_dir
, old_name
);
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();
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
,
1552 FCacheEntry
*dentry
;
1554 char symlink
[MAXPATHLEN
];
1555 char store_name
[MAXPATHLEN
];
1559 ce
= cred_get (fid
.Cell
, getuid(), CRED_ANY
);
1561 strlcpy(store_name
, name
, sizeof(store_name
));
1565 /* set things up so that fname points to the remainder of the path,
1566 * whereas base points to the whatever preceeds the first /
1569 fname
= strchr(fname
, '/');
1571 /* deal with repeated adjacent / chars by eliminating the
1574 while (*fname
== '/') {
1580 /* deal with absolute pathnames first. */
1581 if (*base
== '\0') {
1582 error
= getroot(&cwd
, ce
);
1584 arla_warn(ADEBWARN
, error
, "getroot");
1590 if (strncmp("afs",fname
,3) == 0) {
1598 error
= fcache_get(&dentry
, cwd
, ce
);
1600 arla_warn (ADEBWARN
, error
, "fcache_get");
1604 error
= cm_lookup (&dentry
, base
, &file
, &ce
, TRUE
);
1605 fcache_release(dentry
);
1607 arla_warn (ADEBWARN
, error
, "lookup(%s)", base
);
1612 error
= fcache_get(&entry
, file
, ce
);
1614 arla_warn (ADEBWARN
, error
, "fcache_get");
1619 /* handle symlinks here */
1620 if (entry
->status
.FileType
== TYPE_LINK
) {
1621 error
= fcache_get_data (&entry
, &ce
, 0, 0);
1623 fcache_release(entry
);
1624 arla_warn (ADEBWARN
, error
, "fcache_get_data");
1629 error
= read_symlink(entry
, symlink
, sizeof(symlink
));
1631 fcache_release(entry
);
1632 arla_warn(ADEBWARN
, error
, "read_symlink");
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
));
1647 /* if not a symlink, just update cwd */
1650 fcache_release(entry
);
1652 /* the *fname condition below deals with a trailing / in a
1654 } while (fname
!= NULL
&& *fname
);