Commit a55370a3c0106106a975c5a09cee800611d0cf50

Authored by NeilBrown
Committed by Linus Torvalds
1 parent 7dea9d280c

[PATCH] knfsd: nfsd4: reboot hash

For the purposes of reboot recovery we keep a directory with subdirectories
each having a name that is the ascii hex representation of the md5 sum of a
client identifier for an active client.

This adds the code to calculate that name.  We also use it for the purposes of
comparing clients, so if someone ever manages to find two client names that
are md5 collisions, then we'll return clid_inuse to the second.

Signed-off-by: Andy Adamson <andros@citi.umich.edu>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 5 changed files with 143 additions and 44 deletions Side-by-side Diff

... ... @@ -1413,6 +1413,8 @@
1413 1413 bool "Provide NFSv4 server support (EXPERIMENTAL)"
1414 1414 depends on NFSD_V3 && EXPERIMENTAL
1415 1415 select NFSD_TCP
  1416 + select CRYPTO_MD5
  1417 + select CRYPTO
1416 1418 help
1417 1419 If you would like to include the NFSv4 server as well as the NFSv2
1418 1420 and NFSv3 servers, say Y here. This feature is experimental, and
... ... @@ -10,6 +10,6 @@
10 10 nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
11 11 nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
12 12 nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
13   - nfs4acl.o nfs4callback.o
  13 + nfs4acl.o nfs4callback.o nfs4recover.o
14 14 nfsd-objs := $(nfsd-y)
fs/nfsd/nfs4recover.c
  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 +
  49 +static void
  50 +md5_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 +
  63 +int
  64 +nfs4_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;
  93 +out:
  94 + if (tfm)
  95 + crypto_free_tfm(tfm);
  96 + return status;
  97 +}
... ... @@ -231,8 +231,8 @@
231 231  
232 232 #define clientid_hashval(id) \
233 233 ((id) & CLIENT_HASH_MASK)
234   -#define clientstr_hashval(name, namelen) \
235   - (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)
  234 +#define clientstr_hashval(name) \
  235 + (opaque_hashval((name), 8) & CLIENT_HASH_MASK)
236 236 /*
237 237 * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
238 238 * used in reboot/reset lease grace period processing
239 239  
... ... @@ -366,11 +366,12 @@
366 366 }
367 367  
368 368 static struct nfs4_client *
369   -create_client(struct xdr_netobj name) {
  369 +create_client(struct xdr_netobj name, char *recdir) {
370 370 struct nfs4_client *clp;
371 371  
372 372 if (!(clp = alloc_client(name)))
373 373 goto out;
  374 + memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
374 375 atomic_set(&clp->cl_count, 1);
375 376 atomic_set(&clp->cl_callback.cb_set, 0);
376 377 clp->cl_callback.cb_parsed = 0;
... ... @@ -403,11 +404,9 @@
403 404 get_group_info(target->cr_group_info);
404 405 }
405 406  
406   -static int
407   -cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) {
408   - if (!n1 || !n2)
409   - return 0;
410   - return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len));
  407 +static inline int
  408 +same_name(const char *n1, const char *n2) {
  409 + return 0 == memcmp(n1, n2, HEXDIR_LEN);
411 410 }
412 411  
413 412 static int
... ... @@ -479,8 +478,7 @@
479 478 list_del_init(&clp->cl_strhash);
480 479 list_del_init(&clp->cl_idhash);
481 480 list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]);
482   - strhashval = clientstr_hashval(clp->cl_name.data,
483   - clp->cl_name.len);
  481 + strhashval = clientstr_hashval(clp->cl_recdir);
484 482 list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]);
485 483 renew_client(clp);
486 484 }
487 485  
488 486  
489 487  
... ... @@ -651,22 +649,27 @@
651 649 unsigned int strhashval;
652 650 struct nfs4_client * conf, * unconf, * new, * clp;
653 651 int status;
  652 + char dname[HEXDIR_LEN];
654 653  
655 654 status = nfserr_inval;
656 655 if (!check_name(clname))
657 656 goto out;
658 657  
  658 + status = nfs4_make_rec_clidname(dname, &clname);
  659 + if (status)
  660 + goto out;
  661 +
659 662 /*
660 663 * XXX The Duplicate Request Cache (DRC) has been checked (??)
661 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 669 conf = NULL;
667 670 nfs4_lock_state();
668 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 673 continue;
671 674 /*
672 675 * CASE 0:
... ... @@ -686,7 +689,7 @@
686 689 }
687 690 unconf = NULL;
688 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 693 continue;
691 694 /* cl_name match from a previous SETCLIENTID operation */
692 695 unconf = clp;
... ... @@ -700,7 +703,8 @@
700 703 */
701 704 if (unconf)
702 705 expire_client(unconf);
703   - if (!(new = create_client(clname)))
  706 + new = create_client(clname, dname);
  707 + if (new == NULL)
704 708 goto out;
705 709 copy_verf(new, &clverifier);
706 710 new->cl_addr = ip_addr;
... ... @@ -728,7 +732,8 @@
728 732 cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) {
729 733 expire_client(unconf);
730 734 }
731   - if (!(new = create_client(clname)))
  735 + new = create_client(clname, dname);
  736 + if (new == NULL)
732 737 goto out;
733 738 copy_verf(new,&conf->cl_verifier);
734 739 new->cl_addr = ip_addr;
... ... @@ -746,7 +751,8 @@
746 751 * using input clverifier, clname, and callback info
747 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 756 goto out;
751 757 copy_verf(new,&clverifier);
752 758 new->cl_addr = ip_addr;
... ... @@ -772,7 +778,8 @@
772 778 * new cl_verifier and a new cl_confirm
773 779 */
774 780 expire_client(unconf);
775   - if (!(new = create_client(clname)))
  781 + new = create_client(clname, dname);
  782 + if (new == NULL)
776 783 goto out;
777 784 copy_verf(new,&clverifier);
778 785 new->cl_addr = ip_addr;
... ... @@ -856,7 +863,7 @@
856 863 if ((conf && unconf) &&
857 864 (cmp_verf(&unconf->cl_confirm, &confirm)) &&
858 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 867 (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) {
861 868 if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred))
862 869 status = nfserr_clid_inuse;
... ... @@ -876,7 +883,7 @@
876 883 if ((conf && !unconf) ||
877 884 ((conf && unconf) &&
878 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 887 if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) {
881 888 status = nfserr_clid_inuse;
882 889 } else {
883 890  
884 891  
885 892  
886 893  
887 894  
... ... @@ -3074,39 +3081,28 @@
3074 3081 }
3075 3082  
3076 3083 static inline struct nfs4_client_reclaim *
3077   -alloc_reclaim(int namelen)
  3084 +alloc_reclaim(void)
3078 3085 {
3079   - struct nfs4_client_reclaim *crp = NULL;
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;
  3086 + return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
3090 3087 }
3091 3088  
3092 3089 /*
3093 3090 * failure => all reset bets are off, nfserr_no_grace...
3094 3091 */
3095 3092 static int
3096   -nfs4_client_to_reclaim(char *name, int namlen)
  3093 +nfs4_client_to_reclaim(char *name)
3097 3094 {
3098 3095 unsigned int strhashval;
3099 3096 struct nfs4_client_reclaim *crp = NULL;
3100 3097  
3101   - dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name);
3102   - crp = alloc_reclaim(namlen);
  3098 + dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name);
  3099 + crp = alloc_reclaim();
3103 3100 if (!crp)
3104 3101 return 0;
3105   - strhashval = clientstr_hashval(name, namlen);
  3102 + strhashval = clientstr_hashval(name);
3106 3103 INIT_LIST_HEAD(&crp->cr_strhash);
3107 3104 list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
3108   - memcpy(crp->cr_name.data, name, namlen);
3109   - crp->cr_name.len = namlen;
  3105 + memcpy(crp->cr_recdir, name, HEXDIR_LEN);
3110 3106 reclaim_str_hashtbl_size++;
3111 3107 return 1;
3112 3108 }
... ... @@ -3122,7 +3118,6 @@
3122 3118 crp = list_entry(reclaim_str_hashtbl[i].next,
3123 3119 struct nfs4_client_reclaim, cr_strhash);
3124 3120 list_del(&crp->cr_strhash);
3125   - kfree(crp->cr_name.data);
3126 3121 kfree(crp);
3127 3122 reclaim_str_hashtbl_size--;
3128 3123 }
3129 3124  
3130 3125  
... ... @@ -3145,13 +3140,14 @@
3145 3140 if (clp == NULL)
3146 3141 return NULL;
3147 3142  
3148   - dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n",
3149   - clp->cl_name.len, clp->cl_name.data);
  3143 + dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n",
  3144 + clp->cl_name.len, clp->cl_name.data,
  3145 + clp->cl_recdir);
3150 3146  
3151 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 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 3151 return crp;
3156 3152 }
3157 3153 }
include/linux/nfsd/state.h
... ... @@ -109,6 +109,8 @@
109 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 115 * struct nfs4_client - one per client. Clientids live here.
114 116 * o Each nfs4_client is hashed by clientid.
... ... @@ -126,6 +128,7 @@
126 128 struct list_head cl_del_perclnt; /* list: delegations */
127 129 struct list_head cl_lru; /* tail queue */
128 130 struct xdr_netobj cl_name; /* id generated by client */
  131 + char cl_recdir[HEXDIR_LEN]; /* recovery dir */
129 132 nfs4_verifier cl_verifier; /* generated by client */
130 133 time_t cl_time; /* time of last lease renewal */
131 134 u32 cl_addr; /* client ipaddress */
... ... @@ -143,7 +146,7 @@
143 146 */
144 147 struct nfs4_client_reclaim {
145 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  
149 152 static inline void
... ... @@ -283,6 +286,7 @@
283 286 extern void nfsd4_probe_callback(struct nfs4_client *clp);
284 287 extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
285 288 extern void nfs4_put_delegation(struct nfs4_delegation *dp);
  289 +extern int nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
286 290  
287 291 static inline void
288 292 nfs4_put_stateowner(struct nfs4_stateowner *so)