summaryrefslogtreecommitdiffstats
path: root/fs/afs/dir.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 11:27:50 -0400
committerDavid Howells <dhowells@redhat.com>2017-11-13 10:38:19 -0500
commitd2ddc776a4581d900fc3bdc7803b403daae64d88 (patch)
tree6c5f6bac9e9e9a326ed0a9248914b53c421558e5 /fs/afs/dir.c
parent9cc6fc50f7bc69ac28bee45eed13cbc65a86210f (diff)
afs: Overhaul volume and server record caching and fileserver rotation
The current code assumes that volumes and servers are per-cell and are never shared, but this is not enforced, and, indeed, public cells do exist that are aliases of each other. Further, an organisation can, say, set up a public cell and a private cell with overlapping, but not identical, sets of servers. The difference is purely in the database attached to the VL servers. The current code will malfunction if it sees a server in two cells as it assumes global address -> server record mappings and that each server is in just one cell. Further, each server may have multiple addresses - and may have addresses of different families (IPv4 and IPv6, say). To this end, the following structural changes are made: (1) Server record management is overhauled: (a) Server records are made independent of cell. The namespace keeps track of them, volume records have lists of them and each vnode has a server on which its callback interest currently resides. (b) The cell record no longer keeps a list of servers known to be in that cell. (c) The server records are now kept in a flat list because there's no single address to sort on. (d) Server records are now keyed by their UUID within the namespace. (e) The addresses for a server are obtained with the VL.GetAddrsU rather than with VL.GetEntryByName, using the server's UUID as a parameter. (f) Cached server records are garbage collected after a period of non-use and are counted out of existence before purging is allowed to complete. This protects the work functions against rmmod. (g) The servers list is now in /proc/fs/afs/servers. (2) Volume record management is overhauled: (a) An RCU-replaceable server list is introduced. This tracks both servers and their coresponding callback interests. (b) The superblock is now keyed on cell record and numeric volume ID. (c) The volume record is now tied to the superblock which mounts it, and is activated when mounted and deactivated when unmounted. This makes it easier to handle the cache cookie without causing a double-use in fscache. (d) The volume record is loaded from the VLDB using VL.GetEntryByNameU to get the server UUID list. (e) The volume name is updated if it is seen to have changed when the volume is updated (the update is keyed on the volume ID). (3) The vlocation record is got rid of and VLDB records are no longer cached. Sufficient information is stored in the volume record, though an update to a volume record is now no longer shared between related volumes (volumes come in bundles of three: R/W, R/O and backup). and the following procedural changes are made: (1) The fileserver cursor introduced previously is now fleshed out and used to iterate over fileservers and their addresses. (2) Volume status is checked during iteration, and the server list is replaced if a change is detected. (3) Server status is checked during iteration, and the address list is replaced if a change is detected. (4) The abort code is saved into the address list cursor and -ECONNABORTED returned in afs_make_call() if a remote abort happened rather than translating the abort into an error message. This allows actions to be taken depending on the abort code more easily. (a) If a VMOVED abort is seen then this is handled by rechecking the volume and restarting the iteration. (b) If a VBUSY, VRESTARTING or VSALVAGING abort is seen then this is handled by sleeping for a short period and retrying and/or trying other servers that might serve that volume. A message is also displayed once until the condition has cleared. (c) If a VOFFLINE abort is seen, then this is handled as VBUSY for the moment. (d) If a VNOVOL abort is seen, the volume is rechecked in the VLDB to see if it has been deleted; if not, the fileserver is probably indicating that the volume couldn't be attached and needs salvaging. (e) If statfs() sees one of these aborts, it does not sleep, but rather returns an error, so as not to block the umount program. (5) The fileserver iteration functions in vnode.c are now merged into their callers and more heavily macroised around the cursor. vnode.c is removed. (6) Operations on a particular vnode are serialised on that vnode because the server will lock that vnode whilst it operates on it, so a second op sent will just have to wait. (7) Fileservers are probed with FS.GetCapabilities before being used. This is where service upgrade will be done. (8) A callback interest on a fileserver is set up before an FS operation is performed and passed through to afs_make_call() so that it can be set on the vnode if the operation returns a callback. The callback interest is passed through to afs_iget() also so that it can be set there too. In general, record updating is done on an as-needed basis when we try to access servers, volumes or vnodes rather than offloading it to work items and special threads. Notes: (1) Pre AFS-3.4 servers are no longer supported, though this can be added back if necessary (AFS-3.4 was released in 1998). (2) VBUSY is retried forever for the moment at intervals of 1s. (3) /proc/fs/afs/<cell>/servers no longer exists. Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs/dir.c')
-rw-r--r--fs/afs/dir.c388
1 files changed, 224 insertions, 164 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 37083699a0df..53f3917440e7 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -553,7 +553,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
553 dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; 553 dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version;
554 554
555 /* instantiate the dentry */ 555 /* instantiate the dentry */
556 inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); 556 inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL, NULL);
557 key_put(key); 557 key_put(key);
558 if (IS_ERR(inode)) { 558 if (IS_ERR(inode)) {
559 _leave(" = %ld", PTR_ERR(inode)); 559 _leave(" = %ld", PTR_ERR(inode));
@@ -741,20 +741,48 @@ static void afs_d_release(struct dentry *dentry)
741} 741}
742 742
743/* 743/*
744 * Create a new inode for create/mkdir/symlink
745 */
746static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
747 struct dentry *new_dentry,
748 struct afs_fid *newfid,
749 struct afs_file_status *newstatus,
750 struct afs_callback *newcb)
751{
752 struct inode *inode;
753
754 if (fc->ac.error < 0)
755 return;
756
757 inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key,
758 newfid, newstatus, newcb, fc->cbi);
759 if (IS_ERR(inode)) {
760 /* ENOMEM or EINTR at a really inconvenient time - just abandon
761 * the new directory on the server.
762 */
763 fc->ac.error = PTR_ERR(inode);
764 return;
765 }
766
767 d_instantiate(new_dentry, inode);
768 if (d_unhashed(new_dentry))
769 d_rehash(new_dentry);
770}
771
772/*
744 * create a directory on an AFS filesystem 773 * create a directory on an AFS filesystem
745 */ 774 */
746static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 775static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
747{ 776{
748 struct afs_file_status status; 777 struct afs_file_status newstatus;
749 struct afs_callback cb; 778 struct afs_fs_cursor fc;
750 struct afs_server *server; 779 struct afs_callback newcb;
751 struct afs_vnode *dvnode, *vnode; 780 struct afs_vnode *dvnode = AFS_FS_I(dir);
752 struct afs_fid fid; 781 struct afs_fid newfid;
753 struct inode *inode;
754 struct key *key; 782 struct key *key;
755 int ret; 783 int ret;
756 784
757 dvnode = AFS_FS_I(dir); 785 mode |= S_IFDIR;
758 786
759 _enter("{%x:%u},{%pd},%ho", 787 _enter("{%x:%u},{%pd},%ho",
760 dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); 788 dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
@@ -765,40 +793,27 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
765 goto error; 793 goto error;
766 } 794 }
767 795
768 mode |= S_IFDIR; 796 ret = -ERESTARTSYS;
769 ret = afs_vnode_create(dvnode, key, dentry->d_name.name, 797 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
770 mode, &fid, &status, &cb, &server); 798 while (afs_select_fileserver(&fc)) {
771 if (ret < 0) 799 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
772 goto mkdir_error; 800 afs_fs_create(&fc, dentry->d_name.name, mode,
801 &newfid, &newstatus, &newcb);
802 }
773 803
774 inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); 804 afs_check_for_remote_deletion(&fc, fc.vnode);
775 if (IS_ERR(inode)) { 805 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
776 /* ENOMEM at a really inconvenient time - just abandon the new 806 afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
777 * directory on the server */ 807 ret = afs_end_vnode_operation(&fc);
778 ret = PTR_ERR(inode); 808 if (ret < 0)
779 goto iget_error; 809 goto error_key;
780 } 810 }
781 811
782 /* apply the status report we've got for the new vnode */
783 vnode = AFS_FS_I(inode);
784 spin_lock(&vnode->lock);
785 vnode->update_cnt++;
786 spin_unlock(&vnode->lock);
787 afs_vnode_finalise_status_update(vnode, server);
788 afs_put_server(afs_i2net(dir), server);
789
790 d_instantiate(dentry, inode);
791 if (d_unhashed(dentry)) {
792 _debug("not hashed");
793 d_rehash(dentry);
794 }
795 key_put(key); 812 key_put(key);
796 _leave(" = 0"); 813 _leave(" = 0");
797 return 0; 814 return 0;
798 815
799iget_error: 816error_key:
800 afs_put_server(afs_i2net(dir), server);
801mkdir_error:
802 key_put(key); 817 key_put(key);
803error: 818error:
804 d_drop(dentry); 819 d_drop(dentry);
@@ -807,16 +822,29 @@ error:
807} 822}
808 823
809/* 824/*
825 * Remove a subdir from a directory.
826 */
827static void afs_dir_remove_subdir(struct dentry *dentry)
828{
829 if (d_really_is_positive(dentry)) {
830 struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
831
832 clear_nlink(&vnode->vfs_inode);
833 set_bit(AFS_VNODE_DELETED, &vnode->flags);
834 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
835 }
836}
837
838/*
810 * remove a directory from an AFS filesystem 839 * remove a directory from an AFS filesystem
811 */ 840 */
812static int afs_rmdir(struct inode *dir, struct dentry *dentry) 841static int afs_rmdir(struct inode *dir, struct dentry *dentry)
813{ 842{
814 struct afs_vnode *dvnode, *vnode; 843 struct afs_fs_cursor fc;
844 struct afs_vnode *dvnode = AFS_FS_I(dir);
815 struct key *key; 845 struct key *key;
816 int ret; 846 int ret;
817 847
818 dvnode = AFS_FS_I(dir);
819
820 _enter("{%x:%u},{%pd}", 848 _enter("{%x:%u},{%pd}",
821 dvnode->fid.vid, dvnode->fid.vnode, dentry); 849 dvnode->fid.vid, dvnode->fid.vnode, dentry);
822 850
@@ -826,45 +854,69 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
826 goto error; 854 goto error;
827 } 855 }
828 856
829 ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true); 857 ret = -ERESTARTSYS;
830 if (ret < 0) 858 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
831 goto rmdir_error; 859 while (afs_select_fileserver(&fc)) {
860 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
861 afs_fs_remove(&fc, dentry->d_name.name, true);
862 }
832 863
833 if (d_really_is_positive(dentry)) { 864 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
834 vnode = AFS_FS_I(d_inode(dentry)); 865 ret = afs_end_vnode_operation(&fc);
835 clear_nlink(&vnode->vfs_inode); 866 if (ret == 0)
836 set_bit(AFS_VNODE_DELETED, &vnode->flags); 867 afs_dir_remove_subdir(dentry);
837 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
838 } 868 }
839 869
840 key_put(key); 870 key_put(key);
841 _leave(" = 0");
842 return 0;
843
844rmdir_error:
845 key_put(key);
846error: 871error:
847 _leave(" = %d", ret);
848 return ret; 872 return ret;
849} 873}
850 874
851/* 875/*
852 * remove a file from an AFS filesystem 876 * Remove a link to a file or symlink from a directory.
877 *
878 * If the file was not deleted due to excess hard links, the fileserver will
879 * break the callback promise on the file - if it had one - before it returns
880 * to us, and if it was deleted, it won't
881 *
882 * However, if we didn't have a callback promise outstanding, or it was
883 * outstanding on a different server, then it won't break it either...
884 */
885static int afs_dir_remove_link(struct dentry *dentry, struct key *key)
886{
887 int ret = 0;
888
889 if (d_really_is_positive(dentry)) {
890 struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
891
892 if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
893 kdebug("AFS_VNODE_DELETED");
894 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
895
896 ret = afs_validate(vnode, key);
897 if (ret == -ESTALE)
898 ret = 0;
899 _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
900 }
901
902 return ret;
903}
904
905/*
906 * Remove a file or symlink from an AFS filesystem.
853 */ 907 */
854static int afs_unlink(struct inode *dir, struct dentry *dentry) 908static int afs_unlink(struct inode *dir, struct dentry *dentry)
855{ 909{
856 struct afs_vnode *dvnode, *vnode; 910 struct afs_fs_cursor fc;
911 struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
857 struct key *key; 912 struct key *key;
858 int ret; 913 int ret;
859 914
860 dvnode = AFS_FS_I(dir);
861
862 _enter("{%x:%u},{%pd}", 915 _enter("{%x:%u},{%pd}",
863 dvnode->fid.vid, dvnode->fid.vnode, dentry); 916 dvnode->fid.vid, dvnode->fid.vnode, dentry);
864 917
865 ret = -ENAMETOOLONG;
866 if (dentry->d_name.len >= AFSNAMEMAX) 918 if (dentry->d_name.len >= AFSNAMEMAX)
867 goto error; 919 return -ENAMETOOLONG;
868 920
869 key = afs_request_key(dvnode->volume->cell); 921 key = afs_request_key(dvnode->volume->cell);
870 if (IS_ERR(key)) { 922 if (IS_ERR(key)) {
@@ -872,42 +924,28 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
872 goto error; 924 goto error;
873 } 925 }
874 926
927 /* Try to make sure we have a callback promise on the victim. */
875 if (d_really_is_positive(dentry)) { 928 if (d_really_is_positive(dentry)) {
876 vnode = AFS_FS_I(d_inode(dentry)); 929 vnode = AFS_FS_I(d_inode(dentry));
877
878 /* make sure we have a callback promise on the victim */
879 ret = afs_validate(vnode, key); 930 ret = afs_validate(vnode, key);
880 if (ret < 0) 931 if (ret < 0)
881 goto error; 932 goto error_key;
882 } 933 }
883 934
884 ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); 935 ret = -ERESTARTSYS;
885 if (ret < 0) 936 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
886 goto remove_error; 937 while (afs_select_fileserver(&fc)) {
938 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
939 afs_fs_remove(&fc, dentry->d_name.name, false);
940 }
887 941
888 if (d_really_is_positive(dentry)) { 942 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
889 /* if the file wasn't deleted due to excess hard links, the 943 ret = afs_end_vnode_operation(&fc);
890 * fileserver will break the callback promise on the file - if 944 if (ret == 0)
891 * it had one - before it returns to us, and if it was deleted, 945 ret = afs_dir_remove_link(dentry, key);
892 * it won't
893 *
894 * however, if we didn't have a callback promise outstanding,
895 * or it was outstanding on a different server, then it won't
896 * break it either...
897 */
898 vnode = AFS_FS_I(d_inode(dentry));
899 if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
900 _debug("AFS_VNODE_DELETED");
901 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
902 ret = afs_validate(vnode, key);
903 _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
904 } 946 }
905 947
906 key_put(key); 948error_key:
907 _leave(" = 0");
908 return 0;
909
910remove_error:
911 key_put(key); 949 key_put(key);
912error: 950error:
913 _leave(" = %d", ret); 951 _leave(" = %d", ret);
@@ -920,60 +958,50 @@ error:
920static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, 958static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
921 bool excl) 959 bool excl)
922{ 960{
923 struct afs_file_status status; 961 struct afs_fs_cursor fc;
924 struct afs_callback cb; 962 struct afs_file_status newstatus;
925 struct afs_server *server; 963 struct afs_callback newcb;
926 struct afs_vnode *dvnode, *vnode; 964 struct afs_vnode *dvnode = dvnode = AFS_FS_I(dir);
927 struct afs_fid fid; 965 struct afs_fid newfid;
928 struct inode *inode;
929 struct key *key; 966 struct key *key;
930 int ret; 967 int ret;
931 968
932 dvnode = AFS_FS_I(dir); 969 mode |= S_IFREG;
933 970
934 _enter("{%x:%u},{%pd},%ho,", 971 _enter("{%x:%u},{%pd},%ho,",
935 dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); 972 dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
936 973
974 ret = -ENAMETOOLONG;
975 if (dentry->d_name.len >= AFSNAMEMAX)
976 goto error;
977
937 key = afs_request_key(dvnode->volume->cell); 978 key = afs_request_key(dvnode->volume->cell);
938 if (IS_ERR(key)) { 979 if (IS_ERR(key)) {
939 ret = PTR_ERR(key); 980 ret = PTR_ERR(key);
940 goto error; 981 goto error;
941 } 982 }
942 983
943 mode |= S_IFREG; 984 ret = -ERESTARTSYS;
944 ret = afs_vnode_create(dvnode, key, dentry->d_name.name, 985 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
945 mode, &fid, &status, &cb, &server); 986 while (afs_select_fileserver(&fc)) {
946 if (ret < 0) 987 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
947 goto create_error; 988 afs_fs_create(&fc, dentry->d_name.name, mode,
989 &newfid, &newstatus, &newcb);
990 }
948 991
949 inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); 992 afs_check_for_remote_deletion(&fc, fc.vnode);
950 if (IS_ERR(inode)) { 993 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
951 /* ENOMEM at a really inconvenient time - just abandon the new 994 afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
952 * directory on the server */ 995 ret = afs_end_vnode_operation(&fc);
953 ret = PTR_ERR(inode); 996 if (ret < 0)
954 goto iget_error; 997 goto error_key;
955 } 998 }
956 999
957 /* apply the status report we've got for the new vnode */
958 vnode = AFS_FS_I(inode);
959 spin_lock(&vnode->lock);
960 vnode->update_cnt++;
961 spin_unlock(&vnode->lock);
962 afs_vnode_finalise_status_update(vnode, server);
963 afs_put_server(afs_i2net(dir), server);
964
965 d_instantiate(dentry, inode);
966 if (d_unhashed(dentry)) {
967 _debug("not hashed");
968 d_rehash(dentry);
969 }
970 key_put(key); 1000 key_put(key);
971 _leave(" = 0"); 1001 _leave(" = 0");
972 return 0; 1002 return 0;
973 1003
974iget_error: 1004error_key:
975 afs_put_server(afs_i2net(dir), server);
976create_error:
977 key_put(key); 1005 key_put(key);
978error: 1006error:
979 d_drop(dentry); 1007 d_drop(dentry);
@@ -987,6 +1015,7 @@ error:
987static int afs_link(struct dentry *from, struct inode *dir, 1015static int afs_link(struct dentry *from, struct inode *dir,
988 struct dentry *dentry) 1016 struct dentry *dentry)
989{ 1017{
1018 struct afs_fs_cursor fc;
990 struct afs_vnode *dvnode, *vnode; 1019 struct afs_vnode *dvnode, *vnode;
991 struct key *key; 1020 struct key *key;
992 int ret; 1021 int ret;
@@ -999,23 +1028,45 @@ static int afs_link(struct dentry *from, struct inode *dir,
999 dvnode->fid.vid, dvnode->fid.vnode, 1028 dvnode->fid.vid, dvnode->fid.vnode,
1000 dentry); 1029 dentry);
1001 1030
1031 ret = -ENAMETOOLONG;
1032 if (dentry->d_name.len >= AFSNAMEMAX)
1033 goto error;
1034
1002 key = afs_request_key(dvnode->volume->cell); 1035 key = afs_request_key(dvnode->volume->cell);
1003 if (IS_ERR(key)) { 1036 if (IS_ERR(key)) {
1004 ret = PTR_ERR(key); 1037 ret = PTR_ERR(key);
1005 goto error; 1038 goto error;
1006 } 1039 }
1007 1040
1008 ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name); 1041 ret = -ERESTARTSYS;
1009 if (ret < 0) 1042 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
1010 goto link_error; 1043 if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
1044 afs_end_vnode_operation(&fc);
1045 return -ERESTARTSYS;
1046 }
1047
1048 while (afs_select_fileserver(&fc)) {
1049 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
1050 fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
1051 afs_fs_link(&fc, vnode, dentry->d_name.name);
1052 }
1053
1054 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
1055 afs_vnode_commit_status(&fc, vnode, fc.cb_break_2);
1056 ihold(&vnode->vfs_inode);
1057 d_instantiate(dentry, &vnode->vfs_inode);
1058
1059 mutex_unlock(&vnode->io_lock);
1060 ret = afs_end_vnode_operation(&fc);
1061 if (ret < 0)
1062 goto error_key;
1063 }
1011 1064
1012 ihold(&vnode->vfs_inode);
1013 d_instantiate(dentry, &vnode->vfs_inode);
1014 key_put(key); 1065 key_put(key);
1015 _leave(" = 0"); 1066 _leave(" = 0");
1016 return 0; 1067 return 0;
1017 1068
1018link_error: 1069error_key:
1019 key_put(key); 1070 key_put(key);
1020error: 1071error:
1021 d_drop(dentry); 1072 d_drop(dentry);
@@ -1029,20 +1080,21 @@ error:
1029static int afs_symlink(struct inode *dir, struct dentry *dentry, 1080static int afs_symlink(struct inode *dir, struct dentry *dentry,
1030 const char *content) 1081 const char *content)
1031{ 1082{
1032 struct afs_file_status status; 1083 struct afs_fs_cursor fc;
1033 struct afs_server *server; 1084 struct afs_file_status newstatus;
1034 struct afs_vnode *dvnode, *vnode; 1085 struct afs_vnode *dvnode = AFS_FS_I(dir);
1035 struct afs_fid fid; 1086 struct afs_fid newfid;
1036 struct inode *inode;
1037 struct key *key; 1087 struct key *key;
1038 int ret; 1088 int ret;
1039 1089
1040 dvnode = AFS_FS_I(dir);
1041
1042 _enter("{%x:%u},{%pd},%s", 1090 _enter("{%x:%u},{%pd},%s",
1043 dvnode->fid.vid, dvnode->fid.vnode, dentry, 1091 dvnode->fid.vid, dvnode->fid.vnode, dentry,
1044 content); 1092 content);
1045 1093
1094 ret = -ENAMETOOLONG;
1095 if (dentry->d_name.len >= AFSNAMEMAX)
1096 goto error;
1097
1046 ret = -EINVAL; 1098 ret = -EINVAL;
1047 if (strlen(content) >= AFSPATHMAX) 1099 if (strlen(content) >= AFSPATHMAX)
1048 goto error; 1100 goto error;
@@ -1053,39 +1105,27 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
1053 goto error; 1105 goto error;
1054 } 1106 }
1055 1107
1056 ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content, 1108 ret = -ERESTARTSYS;
1057 &fid, &status, &server); 1109 if (afs_begin_vnode_operation(&fc, dvnode, key)) {
1058 if (ret < 0) 1110 while (afs_select_fileserver(&fc)) {
1059 goto create_error; 1111 fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
1112 afs_fs_symlink(&fc, dentry->d_name.name, content,
1113 &newfid, &newstatus);
1114 }
1060 1115
1061 inode = afs_iget(dir->i_sb, key, &fid, &status, NULL); 1116 afs_check_for_remote_deletion(&fc, fc.vnode);
1062 if (IS_ERR(inode)) { 1117 afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
1063 /* ENOMEM at a really inconvenient time - just abandon the new 1118 afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, NULL);
1064 * directory on the server */ 1119 ret = afs_end_vnode_operation(&fc);
1065 ret = PTR_ERR(inode); 1120 if (ret < 0)
1066 goto iget_error; 1121 goto error_key;
1067 } 1122 }
1068 1123
1069 /* apply the status report we've got for the new vnode */
1070 vnode = AFS_FS_I(inode);
1071 spin_lock(&vnode->lock);
1072 vnode->update_cnt++;
1073 spin_unlock(&vnode->lock);
1074 afs_vnode_finalise_status_update(vnode, server);
1075 afs_put_server(afs_i2net(dir), server);
1076
1077 d_instantiate(dentry, inode);
1078 if (d_unhashed(dentry)) {
1079 _debug("not hashed");
1080 d_rehash(dentry);
1081 }
1082 key_put(key); 1124 key_put(key);
1083 _leave(" = 0"); 1125 _leave(" = 0");
1084 return 0; 1126 return 0;
1085 1127
1086iget_error: 1128error_key:
1087 afs_put_server(afs_i2net(dir), server);
1088create_error:
1089 key_put(key); 1129 key_put(key);
1090error: 1130error:
1091 d_drop(dentry); 1131 d_drop(dentry);
@@ -1100,6 +1140,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
1100 struct inode *new_dir, struct dentry *new_dentry, 1140 struct inode *new_dir, struct dentry *new_dentry,
1101 unsigned int flags) 1141 unsigned int flags)
1102{ 1142{
1143 struct afs_fs_cursor fc;
1103 struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; 1144 struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
1104 struct key *key; 1145 struct key *key;
1105 int ret; 1146 int ret;
@@ -1123,16 +1164,35 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
1123 goto error; 1164 goto error;
1124 } 1165 }
1125 1166
1126 ret = afs_vnode_rename(orig_dvnode, new_dvnode, key, 1167 ret = -ERESTARTSYS;
1127 old_dentry->d_name.name, 1168 if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) {
1128 new_dentry->d_name.name); 1169 if (orig_dvnode != new_dvnode) {
1129 if (ret < 0) 1170 if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
1130 goto rename_error; 1171 afs_end_vnode_operation(&fc);
1172 return -ERESTARTSYS;
1173 }
1174 }
1175 while (afs_select_fileserver(&fc)) {
1176 fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
1177 fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
1178 afs_fs_rename(&fc, old_dentry->d_name.name,
1179 new_dvnode, new_dentry->d_name.name);
1180 }
1181
1182 afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break);
1183 afs_vnode_commit_status(&fc, new_dvnode, fc.cb_break_2);
1184 if (orig_dvnode != new_dvnode)
1185 mutex_unlock(&new_dvnode->io_lock);
1186 ret = afs_end_vnode_operation(&fc);
1187 if (ret < 0)
1188 goto error_key;
1189 }
1190
1131 key_put(key); 1191 key_put(key);
1132 _leave(" = 0"); 1192 _leave(" = 0");
1133 return 0; 1193 return 0;
1134 1194
1135rename_error: 1195error_key:
1136 key_put(key); 1196 key_put(key);
1137error: 1197error:
1138 d_drop(new_dentry); 1198 d_drop(new_dentry);