diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-27 20:00:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-27 20:00:27 -0400 |
commit | 298fb76a5583900a155d387efaf37a8b39e5dea2 (patch) | |
tree | 55b903ec587e8ec470c13084938303f542139557 /fs/nfsd/nfs4recover.c | |
parent | 8f744bdee4fefb17fac052c7418b830de2b59ac8 (diff) | |
parent | e41f9efb85d38d95744b9f35b9903109032b93d4 (diff) |
Merge tag 'nfsd-5.4' of git://linux-nfs.org/~bfields/linux
Pull nfsd updates from Bruce Fields:
"Highlights:
- Add a new knfsd file cache, so that we don't have to open and close
on each (NFSv2/v3) READ or WRITE. This can speed up read and write
in some cases. It also replaces our readahead cache.
- Prevent silent data loss on write errors, by treating write errors
like server reboots for the purposes of write caching, thus forcing
clients to resend their writes.
- Tweak the code that allocates sessions to be more forgiving, so
that NFSv4.1 mounts are less likely to hang when a server already
has a lot of clients.
- Eliminate an arbitrary limit on NFSv4 ACL sizes; they should now be
limited only by the backend filesystem and the maximum RPC size.
- Allow the server to enforce use of the correct kerberos credentials
when a client reclaims state after a reboot.
And some miscellaneous smaller bugfixes and cleanup"
* tag 'nfsd-5.4' of git://linux-nfs.org/~bfields/linux: (34 commits)
sunrpc: clean up indentation issue
nfsd: fix nfs read eof detection
nfsd: Make nfsd_reset_boot_verifier_locked static
nfsd: degraded slot-count more gracefully as allocation nears exhaustion.
nfsd: handle drc over-allocation gracefully.
nfsd: add support for upcall version 2
nfsd: add a "GetVersion" upcall for nfsdcld
nfsd: Reset the boot verifier on all write I/O errors
nfsd: Don't garbage collect files that might contain write errors
nfsd: Support the server resetting the boot verifier
nfsd: nfsd_file cache entries should be per net namespace
nfsd: eliminate an unnecessary acl size limit
Deprecate nfsd fault injection
nfsd: remove duplicated include from filecache.c
nfsd: Fix the documentation for svcxdr_tmpalloc()
nfsd: Fix up some unused variable warnings
nfsd: close cached files prior to a REMOVE or RENAME that would replace target
nfsd: rip out the raparms cache
nfsd: have nfsd_test_lock use the nfsd_file cache
nfsd: hook up nfs4_preprocess_stateid_op to the nfsd_file cache
...
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r-- | fs/nfsd/nfs4recover.c | 388 |
1 files changed, 325 insertions, 63 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 87679557d0d6..cdc75ad4438b 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -59,8 +59,13 @@ struct nfsd4_client_tracking_ops { | |||
59 | void (*remove)(struct nfs4_client *); | 59 | void (*remove)(struct nfs4_client *); |
60 | int (*check)(struct nfs4_client *); | 60 | int (*check)(struct nfs4_client *); |
61 | void (*grace_done)(struct nfsd_net *); | 61 | void (*grace_done)(struct nfsd_net *); |
62 | uint8_t version; | ||
63 | size_t msglen; | ||
62 | }; | 64 | }; |
63 | 65 | ||
66 | static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops; | ||
67 | static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2; | ||
68 | |||
64 | /* Globals */ | 69 | /* Globals */ |
65 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | 70 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; |
66 | 71 | ||
@@ -173,6 +178,7 @@ __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, | |||
173 | const char *dname, int len, struct nfsd_net *nn) | 178 | const char *dname, int len, struct nfsd_net *nn) |
174 | { | 179 | { |
175 | struct xdr_netobj name; | 180 | struct xdr_netobj name; |
181 | struct xdr_netobj princhash = { .len = 0, .data = NULL }; | ||
176 | struct nfs4_client_reclaim *crp; | 182 | struct nfs4_client_reclaim *crp; |
177 | 183 | ||
178 | name.data = kmemdup(dname, len, GFP_KERNEL); | 184 | name.data = kmemdup(dname, len, GFP_KERNEL); |
@@ -182,7 +188,7 @@ __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, | |||
182 | return; | 188 | return; |
183 | } | 189 | } |
184 | name.len = len; | 190 | name.len = len; |
185 | crp = nfs4_client_to_reclaim(name, nn); | 191 | crp = nfs4_client_to_reclaim(name, princhash, nn); |
186 | if (!crp) { | 192 | if (!crp) { |
187 | kfree(name.data); | 193 | kfree(name.data); |
188 | return; | 194 | return; |
@@ -482,6 +488,7 @@ static int | |||
482 | load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) | 488 | load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) |
483 | { | 489 | { |
484 | struct xdr_netobj name; | 490 | struct xdr_netobj name; |
491 | struct xdr_netobj princhash = { .len = 0, .data = NULL }; | ||
485 | 492 | ||
486 | if (child->d_name.len != HEXDIR_LEN - 1) { | 493 | if (child->d_name.len != HEXDIR_LEN - 1) { |
487 | printk("%s: illegal name %pd in recovery directory\n", | 494 | printk("%s: illegal name %pd in recovery directory\n", |
@@ -496,7 +503,7 @@ load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) | |||
496 | goto out; | 503 | goto out; |
497 | } | 504 | } |
498 | name.len = HEXDIR_LEN; | 505 | name.len = HEXDIR_LEN; |
499 | if (!nfs4_client_to_reclaim(name, nn)) | 506 | if (!nfs4_client_to_reclaim(name, princhash, nn)) |
500 | kfree(name.data); | 507 | kfree(name.data); |
501 | out: | 508 | out: |
502 | return 0; | 509 | return 0; |
@@ -718,6 +725,8 @@ static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { | |||
718 | .remove = nfsd4_remove_clid_dir, | 725 | .remove = nfsd4_remove_clid_dir, |
719 | .check = nfsd4_check_legacy_client, | 726 | .check = nfsd4_check_legacy_client, |
720 | .grace_done = nfsd4_recdir_purge_old, | 727 | .grace_done = nfsd4_recdir_purge_old, |
728 | .version = 1, | ||
729 | .msglen = 0, | ||
721 | }; | 730 | }; |
722 | 731 | ||
723 | /* Globals */ | 732 | /* Globals */ |
@@ -731,25 +740,32 @@ struct cld_net { | |||
731 | struct list_head cn_list; | 740 | struct list_head cn_list; |
732 | unsigned int cn_xid; | 741 | unsigned int cn_xid; |
733 | bool cn_has_legacy; | 742 | bool cn_has_legacy; |
743 | struct crypto_shash *cn_tfm; | ||
734 | }; | 744 | }; |
735 | 745 | ||
736 | struct cld_upcall { | 746 | struct cld_upcall { |
737 | struct list_head cu_list; | 747 | struct list_head cu_list; |
738 | struct cld_net *cu_net; | 748 | struct cld_net *cu_net; |
739 | struct completion cu_done; | 749 | struct completion cu_done; |
740 | struct cld_msg cu_msg; | 750 | union { |
751 | struct cld_msg_hdr cu_hdr; | ||
752 | struct cld_msg cu_msg; | ||
753 | struct cld_msg_v2 cu_msg_v2; | ||
754 | } cu_u; | ||
741 | }; | 755 | }; |
742 | 756 | ||
743 | static int | 757 | static int |
744 | __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | 758 | __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) |
745 | { | 759 | { |
746 | int ret; | 760 | int ret; |
747 | struct rpc_pipe_msg msg; | 761 | struct rpc_pipe_msg msg; |
748 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_msg); | 762 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u); |
763 | struct nfsd_net *nn = net_generic(pipe->dentry->d_sb->s_fs_info, | ||
764 | nfsd_net_id); | ||
749 | 765 | ||
750 | memset(&msg, 0, sizeof(msg)); | 766 | memset(&msg, 0, sizeof(msg)); |
751 | msg.data = cmsg; | 767 | msg.data = cmsg; |
752 | msg.len = sizeof(*cmsg); | 768 | msg.len = nn->client_tracking_ops->msglen; |
753 | 769 | ||
754 | ret = rpc_queue_upcall(pipe, &msg); | 770 | ret = rpc_queue_upcall(pipe, &msg); |
755 | if (ret < 0) { | 771 | if (ret < 0) { |
@@ -765,7 +781,7 @@ out: | |||
765 | } | 781 | } |
766 | 782 | ||
767 | static int | 783 | static int |
768 | cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | 784 | cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) |
769 | { | 785 | { |
770 | int ret; | 786 | int ret; |
771 | 787 | ||
@@ -781,11 +797,11 @@ cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | |||
781 | } | 797 | } |
782 | 798 | ||
783 | static ssize_t | 799 | static ssize_t |
784 | __cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg, | 800 | __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, |
785 | struct nfsd_net *nn) | 801 | struct nfsd_net *nn) |
786 | { | 802 | { |
787 | uint8_t cmd; | 803 | uint8_t cmd, princhashlen; |
788 | struct xdr_netobj name; | 804 | struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; |
789 | uint16_t namelen; | 805 | uint16_t namelen; |
790 | struct cld_net *cn = nn->cld_net; | 806 | struct cld_net *cn = nn->cld_net; |
791 | 807 | ||
@@ -794,22 +810,48 @@ __cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg, | |||
794 | return -EFAULT; | 810 | return -EFAULT; |
795 | } | 811 | } |
796 | if (cmd == Cld_GraceStart) { | 812 | if (cmd == Cld_GraceStart) { |
797 | if (get_user(namelen, &cmsg->cm_u.cm_name.cn_len)) | 813 | if (nn->client_tracking_ops->version >= 2) { |
798 | return -EFAULT; | 814 | const struct cld_clntinfo __user *ci; |
799 | name.data = memdup_user(&cmsg->cm_u.cm_name.cn_id, namelen); | 815 | |
800 | if (IS_ERR_OR_NULL(name.data)) | 816 | ci = &cmsg->cm_u.cm_clntinfo; |
801 | return -EFAULT; | 817 | if (get_user(namelen, &ci->cc_name.cn_len)) |
802 | name.len = namelen; | 818 | return -EFAULT; |
819 | name.data = memdup_user(&ci->cc_name.cn_id, namelen); | ||
820 | if (IS_ERR_OR_NULL(name.data)) | ||
821 | return -EFAULT; | ||
822 | name.len = namelen; | ||
823 | get_user(princhashlen, &ci->cc_princhash.cp_len); | ||
824 | if (princhashlen > 0) { | ||
825 | princhash.data = memdup_user( | ||
826 | &ci->cc_princhash.cp_data, | ||
827 | princhashlen); | ||
828 | if (IS_ERR_OR_NULL(princhash.data)) | ||
829 | return -EFAULT; | ||
830 | princhash.len = princhashlen; | ||
831 | } else | ||
832 | princhash.len = 0; | ||
833 | } else { | ||
834 | const struct cld_name __user *cnm; | ||
835 | |||
836 | cnm = &cmsg->cm_u.cm_name; | ||
837 | if (get_user(namelen, &cnm->cn_len)) | ||
838 | return -EFAULT; | ||
839 | name.data = memdup_user(&cnm->cn_id, namelen); | ||
840 | if (IS_ERR_OR_NULL(name.data)) | ||
841 | return -EFAULT; | ||
842 | name.len = namelen; | ||
843 | } | ||
803 | if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { | 844 | if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { |
804 | name.len = name.len - 5; | 845 | name.len = name.len - 5; |
805 | memmove(name.data, name.data + 5, name.len); | 846 | memmove(name.data, name.data + 5, name.len); |
806 | cn->cn_has_legacy = true; | 847 | cn->cn_has_legacy = true; |
807 | } | 848 | } |
808 | if (!nfs4_client_to_reclaim(name, nn)) { | 849 | if (!nfs4_client_to_reclaim(name, princhash, nn)) { |
809 | kfree(name.data); | 850 | kfree(name.data); |
851 | kfree(princhash.data); | ||
810 | return -EFAULT; | 852 | return -EFAULT; |
811 | } | 853 | } |
812 | return sizeof(*cmsg); | 854 | return nn->client_tracking_ops->msglen; |
813 | } | 855 | } |
814 | return -EFAULT; | 856 | return -EFAULT; |
815 | } | 857 | } |
@@ -818,21 +860,22 @@ static ssize_t | |||
818 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | 860 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) |
819 | { | 861 | { |
820 | struct cld_upcall *tmp, *cup; | 862 | struct cld_upcall *tmp, *cup; |
821 | struct cld_msg __user *cmsg = (struct cld_msg __user *)src; | 863 | struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src; |
864 | struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src; | ||
822 | uint32_t xid; | 865 | uint32_t xid; |
823 | struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info, | 866 | struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info, |
824 | nfsd_net_id); | 867 | nfsd_net_id); |
825 | struct cld_net *cn = nn->cld_net; | 868 | struct cld_net *cn = nn->cld_net; |
826 | int16_t status; | 869 | int16_t status; |
827 | 870 | ||
828 | if (mlen != sizeof(*cmsg)) { | 871 | if (mlen != nn->client_tracking_ops->msglen) { |
829 | dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen, | 872 | dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen, |
830 | sizeof(*cmsg)); | 873 | nn->client_tracking_ops->msglen); |
831 | return -EINVAL; | 874 | return -EINVAL; |
832 | } | 875 | } |
833 | 876 | ||
834 | /* copy just the xid so we can try to find that */ | 877 | /* copy just the xid so we can try to find that */ |
835 | if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) { | 878 | if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) { |
836 | dprintk("%s: error when copying xid from userspace", __func__); | 879 | dprintk("%s: error when copying xid from userspace", __func__); |
837 | return -EFAULT; | 880 | return -EFAULT; |
838 | } | 881 | } |
@@ -842,7 +885,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | |||
842 | * list (for -EINPROGRESS, we just want to make sure the xid is | 885 | * list (for -EINPROGRESS, we just want to make sure the xid is |
843 | * valid, not remove the upcall from the list) | 886 | * valid, not remove the upcall from the list) |
844 | */ | 887 | */ |
845 | if (get_user(status, &cmsg->cm_status)) { | 888 | if (get_user(status, &hdr->cm_status)) { |
846 | dprintk("%s: error when copying status from userspace", __func__); | 889 | dprintk("%s: error when copying status from userspace", __func__); |
847 | return -EFAULT; | 890 | return -EFAULT; |
848 | } | 891 | } |
@@ -851,7 +894,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | |||
851 | cup = NULL; | 894 | cup = NULL; |
852 | spin_lock(&cn->cn_lock); | 895 | spin_lock(&cn->cn_lock); |
853 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | 896 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { |
854 | if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) { | 897 | if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) { |
855 | cup = tmp; | 898 | cup = tmp; |
856 | if (status != -EINPROGRESS) | 899 | if (status != -EINPROGRESS) |
857 | list_del_init(&cup->cu_list); | 900 | list_del_init(&cup->cu_list); |
@@ -869,7 +912,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | |||
869 | if (status == -EINPROGRESS) | 912 | if (status == -EINPROGRESS) |
870 | return __cld_pipe_inprogress_downcall(cmsg, nn); | 913 | return __cld_pipe_inprogress_downcall(cmsg, nn); |
871 | 914 | ||
872 | if (copy_from_user(&cup->cu_msg, src, mlen) != 0) | 915 | if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0) |
873 | return -EFAULT; | 916 | return -EFAULT; |
874 | 917 | ||
875 | complete(&cup->cu_done); | 918 | complete(&cup->cu_done); |
@@ -881,7 +924,7 @@ cld_pipe_destroy_msg(struct rpc_pipe_msg *msg) | |||
881 | { | 924 | { |
882 | struct cld_msg *cmsg = msg->data; | 925 | struct cld_msg *cmsg = msg->data; |
883 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, | 926 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, |
884 | cu_msg); | 927 | cu_u.cu_msg); |
885 | 928 | ||
886 | /* errno >= 0 means we got a downcall */ | 929 | /* errno >= 0 means we got a downcall */ |
887 | if (msg->errno >= 0) | 930 | if (msg->errno >= 0) |
@@ -1007,14 +1050,17 @@ nfsd4_remove_cld_pipe(struct net *net) | |||
1007 | 1050 | ||
1008 | nfsd4_cld_unregister_net(net, cn->cn_pipe); | 1051 | nfsd4_cld_unregister_net(net, cn->cn_pipe); |
1009 | rpc_destroy_pipe_data(cn->cn_pipe); | 1052 | rpc_destroy_pipe_data(cn->cn_pipe); |
1053 | if (cn->cn_tfm) | ||
1054 | crypto_free_shash(cn->cn_tfm); | ||
1010 | kfree(nn->cld_net); | 1055 | kfree(nn->cld_net); |
1011 | nn->cld_net = NULL; | 1056 | nn->cld_net = NULL; |
1012 | } | 1057 | } |
1013 | 1058 | ||
1014 | static struct cld_upcall * | 1059 | static struct cld_upcall * |
1015 | alloc_cld_upcall(struct cld_net *cn) | 1060 | alloc_cld_upcall(struct nfsd_net *nn) |
1016 | { | 1061 | { |
1017 | struct cld_upcall *new, *tmp; | 1062 | struct cld_upcall *new, *tmp; |
1063 | struct cld_net *cn = nn->cld_net; | ||
1018 | 1064 | ||
1019 | new = kzalloc(sizeof(*new), GFP_KERNEL); | 1065 | new = kzalloc(sizeof(*new), GFP_KERNEL); |
1020 | if (!new) | 1066 | if (!new) |
@@ -1024,20 +1070,20 @@ alloc_cld_upcall(struct cld_net *cn) | |||
1024 | restart_search: | 1070 | restart_search: |
1025 | spin_lock(&cn->cn_lock); | 1071 | spin_lock(&cn->cn_lock); |
1026 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | 1072 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { |
1027 | if (tmp->cu_msg.cm_xid == cn->cn_xid) { | 1073 | if (tmp->cu_u.cu_msg.cm_xid == cn->cn_xid) { |
1028 | cn->cn_xid++; | 1074 | cn->cn_xid++; |
1029 | spin_unlock(&cn->cn_lock); | 1075 | spin_unlock(&cn->cn_lock); |
1030 | goto restart_search; | 1076 | goto restart_search; |
1031 | } | 1077 | } |
1032 | } | 1078 | } |
1033 | init_completion(&new->cu_done); | 1079 | init_completion(&new->cu_done); |
1034 | new->cu_msg.cm_vers = CLD_UPCALL_VERSION; | 1080 | new->cu_u.cu_msg.cm_vers = nn->client_tracking_ops->version; |
1035 | put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid); | 1081 | put_unaligned(cn->cn_xid++, &new->cu_u.cu_msg.cm_xid); |
1036 | new->cu_net = cn; | 1082 | new->cu_net = cn; |
1037 | list_add(&new->cu_list, &cn->cn_list); | 1083 | list_add(&new->cu_list, &cn->cn_list); |
1038 | spin_unlock(&cn->cn_lock); | 1084 | spin_unlock(&cn->cn_lock); |
1039 | 1085 | ||
1040 | dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid); | 1086 | dprintk("%s: allocated xid %u\n", __func__, new->cu_u.cu_msg.cm_xid); |
1041 | 1087 | ||
1042 | return new; | 1088 | return new; |
1043 | } | 1089 | } |
@@ -1066,20 +1112,20 @@ nfsd4_cld_create(struct nfs4_client *clp) | |||
1066 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 1112 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
1067 | return; | 1113 | return; |
1068 | 1114 | ||
1069 | cup = alloc_cld_upcall(cn); | 1115 | cup = alloc_cld_upcall(nn); |
1070 | if (!cup) { | 1116 | if (!cup) { |
1071 | ret = -ENOMEM; | 1117 | ret = -ENOMEM; |
1072 | goto out_err; | 1118 | goto out_err; |
1073 | } | 1119 | } |
1074 | 1120 | ||
1075 | cup->cu_msg.cm_cmd = Cld_Create; | 1121 | cup->cu_u.cu_msg.cm_cmd = Cld_Create; |
1076 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 1122 | cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; |
1077 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 1123 | memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, |
1078 | clp->cl_name.len); | 1124 | clp->cl_name.len); |
1079 | 1125 | ||
1080 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1126 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1081 | if (!ret) { | 1127 | if (!ret) { |
1082 | ret = cup->cu_msg.cm_status; | 1128 | ret = cup->cu_u.cu_msg.cm_status; |
1083 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 1129 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
1084 | } | 1130 | } |
1085 | 1131 | ||
@@ -1092,6 +1138,75 @@ out_err: | |||
1092 | 1138 | ||
1093 | /* Ask daemon to create a new record */ | 1139 | /* Ask daemon to create a new record */ |
1094 | static void | 1140 | static void |
1141 | nfsd4_cld_create_v2(struct nfs4_client *clp) | ||
1142 | { | ||
1143 | int ret; | ||
1144 | struct cld_upcall *cup; | ||
1145 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
1146 | struct cld_net *cn = nn->cld_net; | ||
1147 | struct cld_msg_v2 *cmsg; | ||
1148 | struct crypto_shash *tfm = cn->cn_tfm; | ||
1149 | struct xdr_netobj cksum; | ||
1150 | char *principal = NULL; | ||
1151 | SHASH_DESC_ON_STACK(desc, tfm); | ||
1152 | |||
1153 | /* Don't upcall if it's already stored */ | ||
1154 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
1155 | return; | ||
1156 | |||
1157 | cup = alloc_cld_upcall(nn); | ||
1158 | if (!cup) { | ||
1159 | ret = -ENOMEM; | ||
1160 | goto out_err; | ||
1161 | } | ||
1162 | |||
1163 | cmsg = &cup->cu_u.cu_msg_v2; | ||
1164 | cmsg->cm_cmd = Cld_Create; | ||
1165 | cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len; | ||
1166 | memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data, | ||
1167 | clp->cl_name.len); | ||
1168 | if (clp->cl_cred.cr_raw_principal) | ||
1169 | principal = clp->cl_cred.cr_raw_principal; | ||
1170 | else if (clp->cl_cred.cr_principal) | ||
1171 | principal = clp->cl_cred.cr_principal; | ||
1172 | if (principal) { | ||
1173 | desc->tfm = tfm; | ||
1174 | cksum.len = crypto_shash_digestsize(tfm); | ||
1175 | cksum.data = kmalloc(cksum.len, GFP_KERNEL); | ||
1176 | if (cksum.data == NULL) { | ||
1177 | ret = -ENOMEM; | ||
1178 | goto out; | ||
1179 | } | ||
1180 | ret = crypto_shash_digest(desc, principal, strlen(principal), | ||
1181 | cksum.data); | ||
1182 | shash_desc_zero(desc); | ||
1183 | if (ret) { | ||
1184 | kfree(cksum.data); | ||
1185 | goto out; | ||
1186 | } | ||
1187 | cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len; | ||
1188 | memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data, | ||
1189 | cksum.data, cksum.len); | ||
1190 | kfree(cksum.data); | ||
1191 | } else | ||
1192 | cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0; | ||
1193 | |||
1194 | ret = cld_pipe_upcall(cn->cn_pipe, cmsg); | ||
1195 | if (!ret) { | ||
1196 | ret = cmsg->cm_status; | ||
1197 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
1198 | } | ||
1199 | |||
1200 | out: | ||
1201 | free_cld_upcall(cup); | ||
1202 | out_err: | ||
1203 | if (ret) | ||
1204 | pr_err("NFSD: Unable to create client record on stable storage: %d\n", | ||
1205 | ret); | ||
1206 | } | ||
1207 | |||
1208 | /* Ask daemon to create a new record */ | ||
1209 | static void | ||
1095 | nfsd4_cld_remove(struct nfs4_client *clp) | 1210 | nfsd4_cld_remove(struct nfs4_client *clp) |
1096 | { | 1211 | { |
1097 | int ret; | 1212 | int ret; |
@@ -1103,20 +1218,20 @@ nfsd4_cld_remove(struct nfs4_client *clp) | |||
1103 | if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 1218 | if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
1104 | return; | 1219 | return; |
1105 | 1220 | ||
1106 | cup = alloc_cld_upcall(cn); | 1221 | cup = alloc_cld_upcall(nn); |
1107 | if (!cup) { | 1222 | if (!cup) { |
1108 | ret = -ENOMEM; | 1223 | ret = -ENOMEM; |
1109 | goto out_err; | 1224 | goto out_err; |
1110 | } | 1225 | } |
1111 | 1226 | ||
1112 | cup->cu_msg.cm_cmd = Cld_Remove; | 1227 | cup->cu_u.cu_msg.cm_cmd = Cld_Remove; |
1113 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 1228 | cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; |
1114 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 1229 | memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, |
1115 | clp->cl_name.len); | 1230 | clp->cl_name.len); |
1116 | 1231 | ||
1117 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1232 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1118 | if (!ret) { | 1233 | if (!ret) { |
1119 | ret = cup->cu_msg.cm_status; | 1234 | ret = cup->cu_u.cu_msg.cm_status; |
1120 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 1235 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
1121 | } | 1236 | } |
1122 | 1237 | ||
@@ -1145,21 +1260,21 @@ nfsd4_cld_check_v0(struct nfs4_client *clp) | |||
1145 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 1260 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
1146 | return 0; | 1261 | return 0; |
1147 | 1262 | ||
1148 | cup = alloc_cld_upcall(cn); | 1263 | cup = alloc_cld_upcall(nn); |
1149 | if (!cup) { | 1264 | if (!cup) { |
1150 | printk(KERN_ERR "NFSD: Unable to check client record on " | 1265 | printk(KERN_ERR "NFSD: Unable to check client record on " |
1151 | "stable storage: %d\n", -ENOMEM); | 1266 | "stable storage: %d\n", -ENOMEM); |
1152 | return -ENOMEM; | 1267 | return -ENOMEM; |
1153 | } | 1268 | } |
1154 | 1269 | ||
1155 | cup->cu_msg.cm_cmd = Cld_Check; | 1270 | cup->cu_u.cu_msg.cm_cmd = Cld_Check; |
1156 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 1271 | cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; |
1157 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 1272 | memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, |
1158 | clp->cl_name.len); | 1273 | clp->cl_name.len); |
1159 | 1274 | ||
1160 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1275 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1161 | if (!ret) { | 1276 | if (!ret) { |
1162 | ret = cup->cu_msg.cm_status; | 1277 | ret = cup->cu_u.cu_msg.cm_status; |
1163 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 1278 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
1164 | } | 1279 | } |
1165 | 1280 | ||
@@ -1217,22 +1332,95 @@ found: | |||
1217 | } | 1332 | } |
1218 | 1333 | ||
1219 | static int | 1334 | static int |
1335 | nfsd4_cld_check_v2(struct nfs4_client *clp) | ||
1336 | { | ||
1337 | struct nfs4_client_reclaim *crp; | ||
1338 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
1339 | struct cld_net *cn = nn->cld_net; | ||
1340 | int status; | ||
1341 | char dname[HEXDIR_LEN]; | ||
1342 | struct xdr_netobj name; | ||
1343 | struct crypto_shash *tfm = cn->cn_tfm; | ||
1344 | struct xdr_netobj cksum; | ||
1345 | char *principal = NULL; | ||
1346 | SHASH_DESC_ON_STACK(desc, tfm); | ||
1347 | |||
1348 | /* did we already find that this client is stable? */ | ||
1349 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
1350 | return 0; | ||
1351 | |||
1352 | /* look for it in the reclaim hashtable otherwise */ | ||
1353 | crp = nfsd4_find_reclaim_client(clp->cl_name, nn); | ||
1354 | if (crp) | ||
1355 | goto found; | ||
1356 | |||
1357 | if (cn->cn_has_legacy) { | ||
1358 | status = nfs4_make_rec_clidname(dname, &clp->cl_name); | ||
1359 | if (status) | ||
1360 | return -ENOENT; | ||
1361 | |||
1362 | name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL); | ||
1363 | if (!name.data) { | ||
1364 | dprintk("%s: failed to allocate memory for name.data\n", | ||
1365 | __func__); | ||
1366 | return -ENOENT; | ||
1367 | } | ||
1368 | name.len = HEXDIR_LEN; | ||
1369 | crp = nfsd4_find_reclaim_client(name, nn); | ||
1370 | kfree(name.data); | ||
1371 | if (crp) | ||
1372 | goto found; | ||
1373 | |||
1374 | } | ||
1375 | return -ENOENT; | ||
1376 | found: | ||
1377 | if (crp->cr_princhash.len) { | ||
1378 | if (clp->cl_cred.cr_raw_principal) | ||
1379 | principal = clp->cl_cred.cr_raw_principal; | ||
1380 | else if (clp->cl_cred.cr_principal) | ||
1381 | principal = clp->cl_cred.cr_principal; | ||
1382 | if (principal == NULL) | ||
1383 | return -ENOENT; | ||
1384 | desc->tfm = tfm; | ||
1385 | cksum.len = crypto_shash_digestsize(tfm); | ||
1386 | cksum.data = kmalloc(cksum.len, GFP_KERNEL); | ||
1387 | if (cksum.data == NULL) | ||
1388 | return -ENOENT; | ||
1389 | status = crypto_shash_digest(desc, principal, strlen(principal), | ||
1390 | cksum.data); | ||
1391 | shash_desc_zero(desc); | ||
1392 | if (status) { | ||
1393 | kfree(cksum.data); | ||
1394 | return -ENOENT; | ||
1395 | } | ||
1396 | if (memcmp(crp->cr_princhash.data, cksum.data, | ||
1397 | crp->cr_princhash.len)) { | ||
1398 | kfree(cksum.data); | ||
1399 | return -ENOENT; | ||
1400 | } | ||
1401 | kfree(cksum.data); | ||
1402 | } | ||
1403 | crp->cr_clp = clp; | ||
1404 | return 0; | ||
1405 | } | ||
1406 | |||
1407 | static int | ||
1220 | nfsd4_cld_grace_start(struct nfsd_net *nn) | 1408 | nfsd4_cld_grace_start(struct nfsd_net *nn) |
1221 | { | 1409 | { |
1222 | int ret; | 1410 | int ret; |
1223 | struct cld_upcall *cup; | 1411 | struct cld_upcall *cup; |
1224 | struct cld_net *cn = nn->cld_net; | 1412 | struct cld_net *cn = nn->cld_net; |
1225 | 1413 | ||
1226 | cup = alloc_cld_upcall(cn); | 1414 | cup = alloc_cld_upcall(nn); |
1227 | if (!cup) { | 1415 | if (!cup) { |
1228 | ret = -ENOMEM; | 1416 | ret = -ENOMEM; |
1229 | goto out_err; | 1417 | goto out_err; |
1230 | } | 1418 | } |
1231 | 1419 | ||
1232 | cup->cu_msg.cm_cmd = Cld_GraceStart; | 1420 | cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart; |
1233 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1421 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1234 | if (!ret) | 1422 | if (!ret) |
1235 | ret = cup->cu_msg.cm_status; | 1423 | ret = cup->cu_u.cu_msg.cm_status; |
1236 | 1424 | ||
1237 | free_cld_upcall(cup); | 1425 | free_cld_upcall(cup); |
1238 | out_err: | 1426 | out_err: |
@@ -1250,17 +1438,17 @@ nfsd4_cld_grace_done_v0(struct nfsd_net *nn) | |||
1250 | struct cld_upcall *cup; | 1438 | struct cld_upcall *cup; |
1251 | struct cld_net *cn = nn->cld_net; | 1439 | struct cld_net *cn = nn->cld_net; |
1252 | 1440 | ||
1253 | cup = alloc_cld_upcall(cn); | 1441 | cup = alloc_cld_upcall(nn); |
1254 | if (!cup) { | 1442 | if (!cup) { |
1255 | ret = -ENOMEM; | 1443 | ret = -ENOMEM; |
1256 | goto out_err; | 1444 | goto out_err; |
1257 | } | 1445 | } |
1258 | 1446 | ||
1259 | cup->cu_msg.cm_cmd = Cld_GraceDone; | 1447 | cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone; |
1260 | cup->cu_msg.cm_u.cm_gracetime = (int64_t)nn->boot_time; | 1448 | cup->cu_u.cu_msg.cm_u.cm_gracetime = (int64_t)nn->boot_time; |
1261 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1449 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1262 | if (!ret) | 1450 | if (!ret) |
1263 | ret = cup->cu_msg.cm_status; | 1451 | ret = cup->cu_u.cu_msg.cm_status; |
1264 | 1452 | ||
1265 | free_cld_upcall(cup); | 1453 | free_cld_upcall(cup); |
1266 | out_err: | 1454 | out_err: |
@@ -1279,16 +1467,16 @@ nfsd4_cld_grace_done(struct nfsd_net *nn) | |||
1279 | struct cld_upcall *cup; | 1467 | struct cld_upcall *cup; |
1280 | struct cld_net *cn = nn->cld_net; | 1468 | struct cld_net *cn = nn->cld_net; |
1281 | 1469 | ||
1282 | cup = alloc_cld_upcall(cn); | 1470 | cup = alloc_cld_upcall(nn); |
1283 | if (!cup) { | 1471 | if (!cup) { |
1284 | ret = -ENOMEM; | 1472 | ret = -ENOMEM; |
1285 | goto out_err; | 1473 | goto out_err; |
1286 | } | 1474 | } |
1287 | 1475 | ||
1288 | cup->cu_msg.cm_cmd = Cld_GraceDone; | 1476 | cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone; |
1289 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 1477 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); |
1290 | if (!ret) | 1478 | if (!ret) |
1291 | ret = cup->cu_msg.cm_status; | 1479 | ret = cup->cu_u.cu_msg.cm_status; |
1292 | 1480 | ||
1293 | free_cld_upcall(cup); | 1481 | free_cld_upcall(cup); |
1294 | out_err: | 1482 | out_err: |
@@ -1337,6 +1525,53 @@ cld_running(struct nfsd_net *nn) | |||
1337 | } | 1525 | } |
1338 | 1526 | ||
1339 | static int | 1527 | static int |
1528 | nfsd4_cld_get_version(struct nfsd_net *nn) | ||
1529 | { | ||
1530 | int ret = 0; | ||
1531 | struct cld_upcall *cup; | ||
1532 | struct cld_net *cn = nn->cld_net; | ||
1533 | uint8_t version; | ||
1534 | |||
1535 | cup = alloc_cld_upcall(nn); | ||
1536 | if (!cup) { | ||
1537 | ret = -ENOMEM; | ||
1538 | goto out_err; | ||
1539 | } | ||
1540 | cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion; | ||
1541 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); | ||
1542 | if (!ret) { | ||
1543 | ret = cup->cu_u.cu_msg.cm_status; | ||
1544 | if (ret) | ||
1545 | goto out_free; | ||
1546 | version = cup->cu_u.cu_msg.cm_u.cm_version; | ||
1547 | dprintk("%s: userspace returned version %u\n", | ||
1548 | __func__, version); | ||
1549 | if (version < 1) | ||
1550 | version = 1; | ||
1551 | else if (version > CLD_UPCALL_VERSION) | ||
1552 | version = CLD_UPCALL_VERSION; | ||
1553 | |||
1554 | switch (version) { | ||
1555 | case 1: | ||
1556 | nn->client_tracking_ops = &nfsd4_cld_tracking_ops; | ||
1557 | break; | ||
1558 | case 2: | ||
1559 | nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2; | ||
1560 | break; | ||
1561 | default: | ||
1562 | break; | ||
1563 | } | ||
1564 | } | ||
1565 | out_free: | ||
1566 | free_cld_upcall(cup); | ||
1567 | out_err: | ||
1568 | if (ret) | ||
1569 | dprintk("%s: Unable to get version from userspace: %d\n", | ||
1570 | __func__, ret); | ||
1571 | return ret; | ||
1572 | } | ||
1573 | |||
1574 | static int | ||
1340 | nfsd4_cld_tracking_init(struct net *net) | 1575 | nfsd4_cld_tracking_init(struct net *net) |
1341 | { | 1576 | { |
1342 | int status; | 1577 | int status; |
@@ -1351,6 +1586,11 @@ nfsd4_cld_tracking_init(struct net *net) | |||
1351 | status = __nfsd4_init_cld_pipe(net); | 1586 | status = __nfsd4_init_cld_pipe(net); |
1352 | if (status) | 1587 | if (status) |
1353 | goto err_shutdown; | 1588 | goto err_shutdown; |
1589 | nn->cld_net->cn_tfm = crypto_alloc_shash("sha256", 0, 0); | ||
1590 | if (IS_ERR(nn->cld_net->cn_tfm)) { | ||
1591 | status = PTR_ERR(nn->cld_net->cn_tfm); | ||
1592 | goto err_remove; | ||
1593 | } | ||
1354 | 1594 | ||
1355 | /* | 1595 | /* |
1356 | * rpc pipe upcalls take 30 seconds to time out, so we don't want to | 1596 | * rpc pipe upcalls take 30 seconds to time out, so we don't want to |
@@ -1368,10 +1608,14 @@ nfsd4_cld_tracking_init(struct net *net) | |||
1368 | goto err_remove; | 1608 | goto err_remove; |
1369 | } | 1609 | } |
1370 | 1610 | ||
1611 | status = nfsd4_cld_get_version(nn); | ||
1612 | if (status == -EOPNOTSUPP) | ||
1613 | pr_warn("NFSD: nfsdcld GetVersion upcall failed. Please upgrade nfsdcld.\n"); | ||
1614 | |||
1371 | status = nfsd4_cld_grace_start(nn); | 1615 | status = nfsd4_cld_grace_start(nn); |
1372 | if (status) { | 1616 | if (status) { |
1373 | if (status == -EOPNOTSUPP) | 1617 | if (status == -EOPNOTSUPP) |
1374 | printk(KERN_WARNING "NFSD: Please upgrade nfsdcld.\n"); | 1618 | pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.\n"); |
1375 | nfs4_release_reclaim(nn); | 1619 | nfs4_release_reclaim(nn); |
1376 | goto err_remove; | 1620 | goto err_remove; |
1377 | } else | 1621 | } else |
@@ -1403,6 +1647,8 @@ static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v0 = { | |||
1403 | .remove = nfsd4_cld_remove, | 1647 | .remove = nfsd4_cld_remove, |
1404 | .check = nfsd4_cld_check_v0, | 1648 | .check = nfsd4_cld_check_v0, |
1405 | .grace_done = nfsd4_cld_grace_done_v0, | 1649 | .grace_done = nfsd4_cld_grace_done_v0, |
1650 | .version = 1, | ||
1651 | .msglen = sizeof(struct cld_msg), | ||
1406 | }; | 1652 | }; |
1407 | 1653 | ||
1408 | /* For newer nfsdcld's */ | 1654 | /* For newer nfsdcld's */ |
@@ -1413,6 +1659,20 @@ static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { | |||
1413 | .remove = nfsd4_cld_remove, | 1659 | .remove = nfsd4_cld_remove, |
1414 | .check = nfsd4_cld_check, | 1660 | .check = nfsd4_cld_check, |
1415 | .grace_done = nfsd4_cld_grace_done, | 1661 | .grace_done = nfsd4_cld_grace_done, |
1662 | .version = 1, | ||
1663 | .msglen = sizeof(struct cld_msg), | ||
1664 | }; | ||
1665 | |||
1666 | /* v2 create/check ops include the principal, if available */ | ||
1667 | static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = { | ||
1668 | .init = nfsd4_cld_tracking_init, | ||
1669 | .exit = nfsd4_cld_tracking_exit, | ||
1670 | .create = nfsd4_cld_create_v2, | ||
1671 | .remove = nfsd4_cld_remove, | ||
1672 | .check = nfsd4_cld_check_v2, | ||
1673 | .grace_done = nfsd4_cld_grace_done, | ||
1674 | .version = 2, | ||
1675 | .msglen = sizeof(struct cld_msg_v2), | ||
1416 | }; | 1676 | }; |
1417 | 1677 | ||
1418 | /* upcall via usermodehelper */ | 1678 | /* upcall via usermodehelper */ |
@@ -1760,6 +2020,8 @@ static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = { | |||
1760 | .remove = nfsd4_umh_cltrack_remove, | 2020 | .remove = nfsd4_umh_cltrack_remove, |
1761 | .check = nfsd4_umh_cltrack_check, | 2021 | .check = nfsd4_umh_cltrack_check, |
1762 | .grace_done = nfsd4_umh_cltrack_grace_done, | 2022 | .grace_done = nfsd4_umh_cltrack_grace_done, |
2023 | .version = 1, | ||
2024 | .msglen = 0, | ||
1763 | }; | 2025 | }; |
1764 | 2026 | ||
1765 | int | 2027 | int |