aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/Kconfig2
-rw-r--r--fs/nfsd/Makefile2
-rw-r--r--fs/nfsd/nfs4recover.c97
-rw-r--r--fs/nfsd/nfs4state.c80
-rw-r--r--include/linux/nfsd/state.h6
5 files changed, 143 insertions, 44 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index a7c0cc3203cb..5c704d05627a 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1413,6 +1413,8 @@ config NFSD_V4
1413 bool "Provide NFSv4 server support (EXPERIMENTAL)" 1413 bool "Provide NFSv4 server support (EXPERIMENTAL)"
1414 depends on NFSD_V3 && EXPERIMENTAL 1414 depends on NFSD_V3 && EXPERIMENTAL
1415 select NFSD_TCP 1415 select NFSD_TCP
1416 select CRYPTO_MD5
1417 select CRYPTO
1416 help 1418 help
1417 If you would like to include the NFSv4 server as well as the NFSv2 1419 If you would like to include the NFSv4 server as well as the NFSv2
1418 and NFSv3 servers, say Y here. This feature is experimental, and 1420 and NFSv3 servers, say Y here. This feature is experimental, and
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile
index 9f043f44c92f..ce341dc76d5e 100644
--- a/fs/nfsd/Makefile
+++ b/fs/nfsd/Makefile
@@ -10,5 +10,5 @@ nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
10nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o 10nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
11nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o 11nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
12nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ 12nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
13 nfs4acl.o nfs4callback.o 13 nfs4acl.o nfs4callback.o nfs4recover.o
14nfsd-objs := $(nfsd-y) 14nfsd-objs := $(nfsd-y)
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
new file mode 100644
index 000000000000..841a305d7948
--- /dev/null
+++ b/fs/nfsd/nfs4recover.c
@@ -0,0 +1,97 @@
1/*
2* linux/fs/nfsd/nfs4recover.c
3*
4* Copyright (c) 2004 The Regents of the University of Michigan.
5* All rights reserved.
6*
7* Andy Adamson <andros@citi.umich.edu>
8*
9* Redistribution and use in source and binary forms, with or without
10* modification, are permitted provided that the following conditions
11* are met:
12*
13* 1. Redistributions of source code must retain the above copyright
14* notice, this list of conditions and the following disclaimer.
15* 2. Redistributions in binary form must reproduce the above copyright
16* notice, this list of conditions and the following disclaimer in the
17* documentation and/or other materials provided with the distribution.
18* 3. Neither the name of the University nor the names of its
19* contributors may be used to endorse or promote products derived
20* from this software without specific prior written permission.
21*
22* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33*
34*/
35
36
37#include <linux/sunrpc/svc.h>
38#include <linux/nfsd/nfsd.h>
39#include <linux/nfs4.h>
40#include <linux/nfsd/state.h>
41#include <linux/nfsd/xdr4.h>
42#include <asm/uaccess.h>
43#include <asm/scatterlist.h>
44#include <linux/crypto.h>
45
46
47#define NFSDDBG_FACILITY NFSDDBG_PROC
48
49static void
50md5_to_hex(char *out, char *md5)
51{
52 int i;
53
54 for (i=0; i<16; i++) {
55 unsigned char c = md5[i];
56
57 *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
58 *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
59 }
60 *out = '\0';
61}
62
63int
64nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
65{
66 struct xdr_netobj cksum;
67 struct crypto_tfm *tfm;
68 struct scatterlist sg[1];
69 int status = nfserr_resource;
70
71 dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
72 clname->len, clname->data);
73 tfm = crypto_alloc_tfm("md5", 0);
74 if (tfm == NULL)
75 goto out;
76 cksum.len = crypto_tfm_alg_digestsize(tfm);
77 cksum.data = kmalloc(cksum.len, GFP_KERNEL);
78 if (cksum.data == NULL)
79 goto out;
80 crypto_digest_init(tfm);
81
82 sg[0].page = virt_to_page(clname->data);
83 sg[0].offset = offset_in_page(clname->data);
84 sg[0].length = clname->len;
85
86 crypto_digest_update(tfm, sg, 1);
87 crypto_digest_final(tfm, cksum.data);
88
89 md5_to_hex(dname, cksum.data);
90
91 kfree(cksum.data);
92 status = nfs_ok;
93out:
94 if (tfm)
95 crypto_free_tfm(tfm);
96 return status;
97}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2a59d176e69a..0be0b37c84e9 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -231,8 +231,8 @@ unhash_delegation(struct nfs4_delegation *dp)
231 231
232#define clientid_hashval(id) \ 232#define clientid_hashval(id) \
233 ((id) & CLIENT_HASH_MASK) 233 ((id) & CLIENT_HASH_MASK)
234#define clientstr_hashval(name, namelen) \ 234#define clientstr_hashval(name) \
235 (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK) 235 (opaque_hashval((name), 8) & CLIENT_HASH_MASK)
236/* 236/*
237 * reclaim_str_hashtbl[] holds known client info from previous reset/reboot 237 * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
238 * used in reboot/reset lease grace period processing 238 * used in reboot/reset lease grace period processing
@@ -366,11 +366,12 @@ expire_client(struct nfs4_client *clp)
366} 366}
367 367
368static struct nfs4_client * 368static struct nfs4_client *
369create_client(struct xdr_netobj name) { 369create_client(struct xdr_netobj name, char *recdir) {
370 struct nfs4_client *clp; 370 struct nfs4_client *clp;
371 371
372 if (!(clp = alloc_client(name))) 372 if (!(clp = alloc_client(name)))
373 goto out; 373 goto out;
374 memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
374 atomic_set(&clp->cl_count, 1); 375 atomic_set(&clp->cl_count, 1);
375 atomic_set(&clp->cl_callback.cb_set, 0); 376 atomic_set(&clp->cl_callback.cb_set, 0);
376 clp->cl_callback.cb_parsed = 0; 377 clp->cl_callback.cb_parsed = 0;
@@ -403,11 +404,9 @@ copy_cred(struct svc_cred *target, struct svc_cred *source) {
403 get_group_info(target->cr_group_info); 404 get_group_info(target->cr_group_info);
404} 405}
405 406
406static int 407static inline int
407cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { 408same_name(const char *n1, const char *n2) {
408 if (!n1 || !n2) 409 return 0 == memcmp(n1, n2, HEXDIR_LEN);
409 return 0;
410 return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len));
411} 410}
412 411
413static int 412static int
@@ -479,8 +478,7 @@ move_to_confirmed(struct nfs4_client *clp)
479 list_del_init(&clp->cl_strhash); 478 list_del_init(&clp->cl_strhash);
480 list_del_init(&clp->cl_idhash); 479 list_del_init(&clp->cl_idhash);
481 list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); 480 list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]);
482 strhashval = clientstr_hashval(clp->cl_name.data, 481 strhashval = clientstr_hashval(clp->cl_recdir);
483 clp->cl_name.len);
484 list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); 482 list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]);
485 renew_client(clp); 483 renew_client(clp);
486} 484}
@@ -651,22 +649,27 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
651 unsigned int strhashval; 649 unsigned int strhashval;
652 struct nfs4_client * conf, * unconf, * new, * clp; 650 struct nfs4_client * conf, * unconf, * new, * clp;
653 int status; 651 int status;
652 char dname[HEXDIR_LEN];
654 653
655 status = nfserr_inval; 654 status = nfserr_inval;
656 if (!check_name(clname)) 655 if (!check_name(clname))
657 goto out; 656 goto out;
658 657
658 status = nfs4_make_rec_clidname(dname, &clname);
659 if (status)
660 goto out;
661
659 /* 662 /*
660 * XXX The Duplicate Request Cache (DRC) has been checked (??) 663 * XXX The Duplicate Request Cache (DRC) has been checked (??)
661 * We get here on a DRC miss. 664 * We get here on a DRC miss.
662 */ 665 */
663 666
664 strhashval = clientstr_hashval(clname.data, clname.len); 667 strhashval = clientstr_hashval(dname);
665 668
666 conf = NULL; 669 conf = NULL;
667 nfs4_lock_state(); 670 nfs4_lock_state();
668 list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) { 671 list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) {
669 if (!cmp_name(&clp->cl_name, &clname)) 672 if (!same_name(clp->cl_recdir, dname))
670 continue; 673 continue;
671 /* 674 /*
672 * CASE 0: 675 * CASE 0:
@@ -686,7 +689,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
686 } 689 }
687 unconf = NULL; 690 unconf = NULL;
688 list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) { 691 list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) {
689 if (!cmp_name(&clp->cl_name, &clname)) 692 if (!same_name(clp->cl_recdir, dname))
690 continue; 693 continue;
691 /* cl_name match from a previous SETCLIENTID operation */ 694 /* cl_name match from a previous SETCLIENTID operation */
692 unconf = clp; 695 unconf = clp;
@@ -700,7 +703,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
700 */ 703 */
701 if (unconf) 704 if (unconf)
702 expire_client(unconf); 705 expire_client(unconf);
703 if (!(new = create_client(clname))) 706 new = create_client(clname, dname);
707 if (new == NULL)
704 goto out; 708 goto out;
705 copy_verf(new, &clverifier); 709 copy_verf(new, &clverifier);
706 new->cl_addr = ip_addr; 710 new->cl_addr = ip_addr;
@@ -728,7 +732,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
728 cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) { 732 cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) {
729 expire_client(unconf); 733 expire_client(unconf);
730 } 734 }
731 if (!(new = create_client(clname))) 735 new = create_client(clname, dname);
736 if (new == NULL)
732 goto out; 737 goto out;
733 copy_verf(new,&conf->cl_verifier); 738 copy_verf(new,&conf->cl_verifier);
734 new->cl_addr = ip_addr; 739 new->cl_addr = ip_addr;
@@ -746,7 +751,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
746 * using input clverifier, clname, and callback info 751 * using input clverifier, clname, and callback info
747 * and generate a new cl_clientid and cl_confirm. 752 * and generate a new cl_clientid and cl_confirm.
748 */ 753 */
749 if (!(new = create_client(clname))) 754 new = create_client(clname, dname);
755 if (new == NULL)
750 goto out; 756 goto out;
751 copy_verf(new,&clverifier); 757 copy_verf(new,&clverifier);
752 new->cl_addr = ip_addr; 758 new->cl_addr = ip_addr;
@@ -772,7 +778,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
772 * new cl_verifier and a new cl_confirm 778 * new cl_verifier and a new cl_confirm
773 */ 779 */
774 expire_client(unconf); 780 expire_client(unconf);
775 if (!(new = create_client(clname))) 781 new = create_client(clname, dname);
782 if (new == NULL)
776 goto out; 783 goto out;
777 copy_verf(new,&clverifier); 784 copy_verf(new,&clverifier);
778 new->cl_addr = ip_addr; 785 new->cl_addr = ip_addr;
@@ -856,7 +863,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi
856 if ((conf && unconf) && 863 if ((conf && unconf) &&
857 (cmp_verf(&unconf->cl_confirm, &confirm)) && 864 (cmp_verf(&unconf->cl_confirm, &confirm)) &&
858 (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) && 865 (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) &&
859 (cmp_name(&conf->cl_name,&unconf->cl_name)) && 866 (same_name(conf->cl_recdir,unconf->cl_recdir)) &&
860 (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) { 867 (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) {
861 if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred)) 868 if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred))
862 status = nfserr_clid_inuse; 869 status = nfserr_clid_inuse;
@@ -876,7 +883,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi
876 if ((conf && !unconf) || 883 if ((conf && !unconf) ||
877 ((conf && unconf) && 884 ((conf && unconf) &&
878 (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) || 885 (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) ||
879 !cmp_name(&conf->cl_name, &unconf->cl_name)))) { 886 !same_name(conf->cl_recdir, unconf->cl_recdir)))) {
880 if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) { 887 if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) {
881 status = nfserr_clid_inuse; 888 status = nfserr_clid_inuse;
882 } else { 889 } else {
@@ -3074,39 +3081,28 @@ out:
3074} 3081}
3075 3082
3076static inline struct nfs4_client_reclaim * 3083static inline struct nfs4_client_reclaim *
3077alloc_reclaim(int namelen) 3084alloc_reclaim(void)
3078{ 3085{
3079 struct nfs4_client_reclaim *crp = NULL; 3086 return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
3080
3081 crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
3082 if (!crp)
3083 return NULL;
3084 crp->cr_name.data = kmalloc(namelen, GFP_KERNEL);
3085 if (!crp->cr_name.data) {
3086 kfree(crp);
3087 return NULL;
3088 }
3089 return crp;
3090} 3087}
3091 3088
3092/* 3089/*
3093 * failure => all reset bets are off, nfserr_no_grace... 3090 * failure => all reset bets are off, nfserr_no_grace...
3094 */ 3091 */
3095static int 3092static int
3096nfs4_client_to_reclaim(char *name, int namlen) 3093nfs4_client_to_reclaim(char *name)
3097{ 3094{
3098 unsigned int strhashval; 3095 unsigned int strhashval;
3099 struct nfs4_client_reclaim *crp = NULL; 3096 struct nfs4_client_reclaim *crp = NULL;
3100 3097
3101 dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name); 3098 dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name);
3102 crp = alloc_reclaim(namlen); 3099 crp = alloc_reclaim();
3103 if (!crp) 3100 if (!crp)
3104 return 0; 3101 return 0;
3105 strhashval = clientstr_hashval(name, namlen); 3102 strhashval = clientstr_hashval(name);
3106 INIT_LIST_HEAD(&crp->cr_strhash); 3103 INIT_LIST_HEAD(&crp->cr_strhash);
3107 list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); 3104 list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
3108 memcpy(crp->cr_name.data, name, namlen); 3105 memcpy(crp->cr_recdir, name, HEXDIR_LEN);
3109 crp->cr_name.len = namlen;
3110 reclaim_str_hashtbl_size++; 3106 reclaim_str_hashtbl_size++;
3111 return 1; 3107 return 1;
3112} 3108}
@@ -3122,7 +3118,6 @@ nfs4_release_reclaim(void)
3122 crp = list_entry(reclaim_str_hashtbl[i].next, 3118 crp = list_entry(reclaim_str_hashtbl[i].next,
3123 struct nfs4_client_reclaim, cr_strhash); 3119 struct nfs4_client_reclaim, cr_strhash);
3124 list_del(&crp->cr_strhash); 3120 list_del(&crp->cr_strhash);
3125 kfree(crp->cr_name.data);
3126 kfree(crp); 3121 kfree(crp);
3127 reclaim_str_hashtbl_size--; 3122 reclaim_str_hashtbl_size--;
3128 } 3123 }
@@ -3145,13 +3140,14 @@ nfs4_find_reclaim_client(clientid_t *clid)
3145 if (clp == NULL) 3140 if (clp == NULL)
3146 return NULL; 3141 return NULL;
3147 3142
3148 dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n", 3143 dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n",
3149 clp->cl_name.len, clp->cl_name.data); 3144 clp->cl_name.len, clp->cl_name.data,
3145 clp->cl_recdir);
3150 3146
3151 /* find clp->cl_name in reclaim_str_hashtbl */ 3147 /* find clp->cl_name in reclaim_str_hashtbl */
3152 strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); 3148 strhashval = clientstr_hashval(clp->cl_recdir);
3153 list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { 3149 list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
3154 if (cmp_name(&crp->cr_name, &clp->cl_name)) { 3150 if (same_name(crp->cr_recdir, clp->cl_recdir)) {
3155 return crp; 3151 return crp;
3156 } 3152 }
3157 } 3153 }
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index 296e6429fc3b..fdaa84addadb 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -109,6 +109,8 @@ struct nfs4_callback {
109 struct rpc_clnt * cb_client; 109 struct rpc_clnt * cb_client;
110}; 110};
111 111
112#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */
113
112/* 114/*
113 * struct nfs4_client - one per client. Clientids live here. 115 * struct nfs4_client - one per client. Clientids live here.
114 * o Each nfs4_client is hashed by clientid. 116 * o Each nfs4_client is hashed by clientid.
@@ -126,6 +128,7 @@ struct nfs4_client {
126 struct list_head cl_del_perclnt; /* list: delegations */ 128 struct list_head cl_del_perclnt; /* list: delegations */
127 struct list_head cl_lru; /* tail queue */ 129 struct list_head cl_lru; /* tail queue */
128 struct xdr_netobj cl_name; /* id generated by client */ 130 struct xdr_netobj cl_name; /* id generated by client */
131 char cl_recdir[HEXDIR_LEN]; /* recovery dir */
129 nfs4_verifier cl_verifier; /* generated by client */ 132 nfs4_verifier cl_verifier; /* generated by client */
130 time_t cl_time; /* time of last lease renewal */ 133 time_t cl_time; /* time of last lease renewal */
131 u32 cl_addr; /* client ipaddress */ 134 u32 cl_addr; /* client ipaddress */
@@ -143,7 +146,7 @@ struct nfs4_client {
143 */ 146 */
144struct nfs4_client_reclaim { 147struct nfs4_client_reclaim {
145 struct list_head cr_strhash; /* hash by cr_name */ 148 struct list_head cr_strhash; /* hash by cr_name */
146 struct xdr_netobj cr_name; /* id generated by client */ 149 char cr_recdir[HEXDIR_LEN]; /* recover dir */
147}; 150};
148 151
149static inline void 152static inline void
@@ -283,6 +286,7 @@ extern void nfs4_free_stateowner(struct kref *kref);
283extern void nfsd4_probe_callback(struct nfs4_client *clp); 286extern void nfsd4_probe_callback(struct nfs4_client *clp);
284extern void nfsd4_cb_recall(struct nfs4_delegation *dp); 287extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
285extern void nfs4_put_delegation(struct nfs4_delegation *dp); 288extern void nfs4_put_delegation(struct nfs4_delegation *dp);
289extern int nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
286 290
287static inline void 291static inline void
288nfs4_put_stateowner(struct nfs4_stateowner *so) 292nfs4_put_stateowner(struct nfs4_stateowner *so)