[PATCH] knfsd cleanups
[opensuse:kernel.git] / fs / nfsd / export.c
1 #define MSNFS   /* HACK HACK */
2 /*
3  * linux/fs/nfsd/export.c
4  *
5  * NFS exporting and validation.
6  *
7  * We maintain a list of clients, each of which has a list of
8  * exports. To export an fs to a given client, you first have
9  * to create the client entry with NFSCTL_ADDCLIENT, which
10  * creates a client control block and adds it to the hash
11  * table. Then, you call NFSCTL_EXPORT for each fs.
12  *
13  *
14  * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
15  */
16
17 #include <linux/unistd.h>
18 #include <linux/slab.h>
19 #include <linux/sched.h>
20 #include <linux/stat.h>
21 #include <linux/in.h>
22
23 #include <linux/sunrpc/svc.h>
24 #include <linux/nfsd/nfsd.h>
25 #include <linux/nfsd/nfsfh.h>
26 #include <linux/nfsd/syscall.h>
27 #include <linux/lockd/bind.h>
28
29 #define NFSDDBG_FACILITY        NFSDDBG_EXPORT
30 #define NFSD_PARANOIA 1
31
32 typedef struct svc_client       svc_client;
33 typedef struct svc_export       svc_export;
34
35 static svc_export *     exp_find(svc_client *clp, kdev_t dev);
36 static svc_export *     exp_parent(svc_client *clp, kdev_t dev,
37                                         struct dentry *dentry);
38 static svc_export *     exp_child(svc_client *clp, kdev_t dev,
39                                         struct dentry *dentry);
40 static void             exp_unexport_all(svc_client *clp);
41 static void             exp_do_unexport(svc_export *unexp);
42 static svc_client *     exp_getclientbyname(char *name);
43 static void             exp_freeclient(svc_client *clp);
44 static void             exp_unhashclient(svc_client *clp);
45 static int              exp_verify_string(char *cp, int max);
46
47 #define CLIENT_HASHBITS         6
48 #define CLIENT_HASHMAX          (1 << CLIENT_HASHBITS)
49 #define CLIENT_HASHMASK         (CLIENT_HASHMAX - 1)
50 #define CLIENT_HASH(a) \
51                 ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
52 /* XXX: is this adequate for 32bit kdev_t ? */
53 #define EXPORT_HASH(dev)        (minor(dev) & (NFSCLNT_EXPMAX - 1))
54
55 struct svc_clnthash {
56         struct svc_clnthash *   h_next;
57         struct in_addr          h_addr;
58         struct svc_client *     h_client;
59 };
60 static struct svc_clnthash *    clnt_hash[CLIENT_HASHMAX];
61 static svc_client *             clients;
62 static int                      initialized;
63
64 static int                      hash_lock;
65 static int                      want_lock;
66 static int                      hash_count;
67 static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
68
69
70 /*
71  * Find a client's export for a device.
72  */
73 static inline svc_export *
74 exp_find(svc_client *clp, kdev_t dev)
75 {
76         svc_export *    exp;
77
78         exp = clp->cl_export[EXPORT_HASH(dev)];
79         while (exp && !kdev_same(exp->ex_dev, dev))
80                 exp = exp->ex_next;
81         return exp;
82 }
83
84 /*
85  * Find the client's export entry matching xdev/xino.
86  */
87 svc_export *
88 exp_get(svc_client *clp, kdev_t dev, ino_t ino)
89 {
90         svc_export *    exp;
91
92         if (!clp)
93                 return NULL;
94
95         exp = clp->cl_export[EXPORT_HASH(dev)];
96         if (exp)
97                 do {
98                         if (exp->ex_ino == ino && kdev_same(exp->ex_dev, dev))
99                                 goto out;
100                 } while (NULL != (exp = exp->ex_next));
101         exp = NULL;
102 out:
103         return exp;
104 }
105
106 /*
107  * Find the export entry for a given dentry.  <gam3@acm.org>
108  */
109 static svc_export *
110 exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
111 {
112         svc_export      *exp;
113
114         if (clp == NULL)
115                 return NULL;
116
117         for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next)
118                 if (is_subdir(dentry, exp->ex_dentry))
119                         break;
120         return exp;
121 }
122
123 /*
124  * Find the child export entry for a given fs. This function is used
125  * only by the export syscall to keep the export tree consistent.
126  * <gam3@acm.org>
127  */
128 static svc_export *
129 exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
130 {
131         svc_export      *exp;
132
133         if (clp == NULL)
134                 return NULL;
135
136         for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next) {
137                 struct dentry   *ndentry = exp->ex_dentry;
138                 if (ndentry && is_subdir(ndentry->d_parent, dentry))
139                         break;
140         }
141         return exp;
142 }
143
144 /*
145  * Export a file system.
146  */
147 int
148 exp_export(struct nfsctl_export *nxp)
149 {
150         svc_client      *clp;
151         svc_export      *exp, *parent;
152         svc_export      **head;
153         struct nameidata nd;
154         struct inode    *inode = NULL;
155         int             i, err;
156         kdev_t          dev;
157         ino_t           ino;
158
159         /* Consistency check */
160         err = -EINVAL;
161         if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
162             !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
163                 goto out;
164
165         dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
166                         nxp->ex_client, nxp->ex_path,
167                         nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags);
168         dev = to_kdev_t(nxp->ex_dev);
169         ino = nxp->ex_ino;
170
171         /* Try to lock the export table for update */
172         if ((err = exp_writelock()) < 0)
173                 goto out;
174
175         /* Look up client info */
176         err = -EINVAL;
177         if (!(clp = exp_getclientbyname(nxp->ex_client)))
178                 goto out_unlock;
179
180         /*
181          * If there's already an export for this file, assume this
182          * is just a flag update.
183          */
184         if ((exp = exp_get(clp, dev, ino)) != NULL) {
185                 exp->ex_flags    = nxp->ex_flags;
186                 exp->ex_anon_uid = nxp->ex_anon_uid;
187                 exp->ex_anon_gid = nxp->ex_anon_gid;
188                 err = 0;
189                 goto out_unlock;
190         }
191
192         /* Look up the dentry */
193         err = 0;
194         if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd))
195                 err = path_walk(nxp->ex_path, &nd);
196         if (err)
197                 goto out_unlock;
198
199         inode = nd.dentry->d_inode;
200         err = -EINVAL;
201         if (!kdev_same(inode->i_dev, dev) || inode->i_ino != nxp->ex_ino) {
202                 printk(KERN_DEBUG "exp_export: i_dev = %02x:%02x, dev = %02x:%02x\n",
203                         major(inode->i_dev), minor(inode->i_dev),
204                         major(dev), minor(dev)); 
205                 /* I'm just being paranoid... */
206                 goto finish;
207         }
208
209         /* We currently export only dirs and regular files.
210          * This is what umountd does.
211          */
212         err = -ENOTDIR;
213         if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
214                 goto finish;
215
216         err = -EINVAL;
217         if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) ||
218             (inode->i_sb->s_op->read_inode == NULL
219              && inode->i_sb->s_op->fh_to_dentry == NULL)) {
220                 dprintk("exp_export: export of invalid fs type.\n");
221                 goto finish;
222         }
223
224         if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) {
225                 dprintk("exp_export: export not valid (Rule 3).\n");
226                 goto finish;
227         }
228         /* Is this is a sub-export, must be a proper subset of FS */
229         if ((parent = exp_parent(clp, dev, nd.dentry)) != NULL) {
230                 dprintk("exp_export: sub-export not valid (Rule 2).\n");
231                 goto finish;
232         }
233
234         err = -ENOMEM;
235         if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
236                 goto finish;
237         dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
238
239         strcpy(exp->ex_path, nxp->ex_path);
240         exp->ex_client = clp;
241         exp->ex_parent = parent;
242         exp->ex_dentry = nd.dentry;
243         exp->ex_mnt = nd.mnt;
244         exp->ex_flags = nxp->ex_flags;
245         exp->ex_dev = dev;
246         exp->ex_ino = ino;
247         exp->ex_anon_uid = nxp->ex_anon_uid;
248         exp->ex_anon_gid = nxp->ex_anon_gid;
249
250         /* Update parent pointers of all exports */
251         if (parent) {
252                 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
253                         svc_export *temp = clp->cl_export[i];
254
255                         while (temp) {
256                                 if (temp->ex_parent == parent)
257                                         temp->ex_parent = exp;
258                                 temp = temp->ex_next;
259                         }
260                 }
261         }
262
263         head = clp->cl_export + EXPORT_HASH(dev);
264         exp->ex_next = *head;
265         *head = exp;
266
267         err = 0;
268
269         /* Unlock hashtable */
270 out_unlock:
271         exp_unlock();
272 out:
273         return err;
274
275         /* Release the dentry */
276 finish:
277         path_release(&nd);
278         goto out_unlock;
279 }
280
281 /*
282  * Unexport a file system. The export entry has already
283  * been removed from the client's list of exported fs's.
284  */
285 static void
286 exp_do_unexport(svc_export *unexp)
287 {
288         svc_export      *exp;
289         svc_client      *clp;
290         struct dentry   *dentry;
291         struct vfsmount *mnt;
292         struct inode    *inode;
293         int             i;
294
295         /* Update parent pointers. */
296         clp = unexp->ex_client;
297         for (i = 0; i < NFSCLNT_EXPMAX; i++) {
298                 for (exp = clp->cl_export[i]; exp; exp = exp->ex_next)
299                         if (exp->ex_parent == unexp)
300                                 exp->ex_parent = unexp->ex_parent;
301         }
302
303         dentry = unexp->ex_dentry;
304         mnt = unexp->ex_mnt;
305         inode = dentry->d_inode;
306         if (!kdev_same(unexp->ex_dev, inode->i_dev) || unexp->ex_ino != inode->i_ino)
307                 printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
308         dput(dentry);
309         mntput(mnt);
310
311         kfree(unexp);
312 }
313
314 /*
315  * Revoke all exports for a given client.
316  * This may look very awkward, but we have to do it this way in order
317  * to avoid race conditions (aka mind the parent pointer).
318  */
319 static void
320 exp_unexport_all(svc_client *clp)
321 {
322         svc_export      *exp;
323         int             i;
324
325         dprintk("unexporting all fs's for clnt %p\n", clp);
326         for (i = 0; i < NFSCLNT_EXPMAX; i++) {
327                 exp = clp->cl_export[i];
328                 clp->cl_export[i] = NULL;
329                 while (exp) {
330                         svc_export *next = exp->ex_next;
331                         exp_do_unexport(exp);
332                         exp = next;
333                 }
334         }
335 }
336
337 /*
338  * unexport syscall.
339  */
340 int
341 exp_unexport(struct nfsctl_export *nxp)
342 {
343         svc_client      *clp;
344         svc_export      **expp, *exp = NULL;
345         int             err;
346
347         /* Consistency check */
348         if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
349                 return -EINVAL;
350
351         if ((err = exp_writelock()) < 0)
352                 goto out;
353
354         err = -EINVAL;
355         clp = exp_getclientbyname(nxp->ex_client);
356         if (clp) {
357                 kdev_t ex_dev = to_kdev_t(nxp->ex_dev);
358                 expp = clp->cl_export + EXPORT_HASH(ex_dev);
359                 while ((exp = *expp) != NULL) {
360                         if (kdev_same(exp->ex_dev, ex_dev)) {
361                                 if (exp->ex_ino == nxp->ex_ino) {
362                                         *expp = exp->ex_next;
363                                         exp_do_unexport(exp);
364                                         err = 0;
365                                         break;
366                                 }
367                         }
368                         expp = &(exp->ex_next);
369                 }
370         }
371
372         exp_unlock();
373 out:
374         return err;
375 }
376
377 /*
378  * Obtain the root fh on behalf of a client.
379  * This could be done in user space, but I feel that it adds some safety
380  * since its harder to fool a kernel module than a user space program.
381  */
382 int
383 exp_rootfh(struct svc_client *clp, char *path, struct knfsd_fh *f, int maxsize)
384 {
385         struct svc_export       *exp;
386         struct nameidata        nd;
387         struct inode            *inode;
388         struct svc_fh           fh;
389         kdev_t                  dev;
390         int                     err;
391
392         err = -EPERM;
393         /* NB: we probably ought to check that it's NUL-terminated */
394         if (path_init(path, LOOKUP_POSITIVE, &nd) &&
395             path_walk(path, &nd)) {
396                 printk("nfsd: exp_rootfh path not found %s", path);
397                 return err;
398         }
399         inode = nd.dentry->d_inode;
400         dev = inode->i_dev;
401
402         dprintk("nfsd: exp_rootfh(%s [%p] %s:%02x:%02x/%ld)\n",
403                  path, nd.dentry, clp->cl_ident,
404                  major(dev), minor(dev), (long) inode->i_ino);
405         exp = exp_parent(clp, dev, nd.dentry);
406         if (!exp) {
407                 dprintk("nfsd: exp_rootfh export not found.\n");
408                 goto out;
409         }
410
411         /*
412          * fh must be initialized before calling fh_compose
413          */
414         fh_init(&fh, maxsize);
415         if (fh_compose(&fh, exp, dget(nd.dentry), NULL))
416                 err = -EINVAL;
417         else
418                 err = 0;
419         memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
420         fh_put(&fh);
421
422 out:
423         if (path)
424                 path_release(&nd);
425         return err;
426 }
427
428 /*
429  * Hashtable locking. Write locks are placed only by user processes
430  * wanting to modify export information.
431  */
432 void
433 exp_readlock(void)
434 {
435         while (hash_lock || want_lock)
436                 sleep_on(&hash_wait);
437         hash_count++;
438 }
439
440 int
441 exp_writelock(void)
442 {
443         /* fast track */
444         if (!hash_count && !hash_lock) {
445         lock_it:
446                 hash_lock = 1;
447                 return 0;
448         }
449
450         clear_thread_flag(TIF_SIGPENDING);
451         want_lock++;
452         while (hash_count || hash_lock) {
453                 interruptible_sleep_on(&hash_wait);
454                 if (signal_pending(current))
455                         break;
456         }
457         want_lock--;
458
459         /* restore the task's signals */
460         spin_lock_irq(&current->sigmask_lock);
461         recalc_sigpending(current);
462         spin_unlock_irq(&current->sigmask_lock);
463
464         if (!hash_count && !hash_lock)
465                 goto lock_it;
466         return -EINTR;
467 }
468
469 void
470 exp_unlock(void)
471 {
472         if (!hash_count && !hash_lock)
473                 printk(KERN_WARNING "exp_unlock: not locked!\n");
474         if (hash_count)
475                 hash_count--;
476         else
477                 hash_lock = 0;
478         wake_up(&hash_wait);
479 }
480
481 /*
482  * Find a valid client given an inet address. We always move the most
483  * recently used client to the front of the hash chain to speed up
484  * future lookups.
485  * Locking against other processes is the responsibility of the caller.
486  */
487 struct svc_client *
488 exp_getclient(struct sockaddr_in *sin)
489 {
490         struct svc_clnthash     **hp, **head, *tmp;
491         unsigned long           addr = sin->sin_addr.s_addr;
492
493         if (!initialized)
494                 return NULL;
495
496         head = &clnt_hash[CLIENT_HASH(addr)];
497
498         for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
499                 if (tmp->h_addr.s_addr == addr) {
500                         /* Move client to the front */
501                         if (head != hp) {
502                                 *hp = tmp->h_next;
503                                 tmp->h_next = *head;
504                                 *head = tmp;
505                         }
506
507                         return tmp->h_client;
508                 }
509         }
510
511         return NULL;
512 }
513
514 /*
515  * Find a client given its identifier.
516  */
517 static svc_client *
518 exp_getclientbyname(char *ident)
519 {
520         svc_client *    clp;
521
522         for (clp = clients; clp; clp = clp->cl_next) {
523                 if (!strcmp(clp->cl_ident, ident))
524                         return clp;
525         }
526         return NULL;
527 }
528
529 struct flags {
530         int flag;
531         char *name[2];
532 } expflags[] = {
533         { NFSEXP_READONLY, {"ro", "rw"}},
534         { NFSEXP_INSECURE_PORT, {"insecure", ""}},
535         { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
536         { NFSEXP_ALLSQUASH, {"all_squash", ""}},
537         { NFSEXP_ASYNC, {"async", "sync"}},
538         { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}},
539         { NFSEXP_UIDMAP, {"uidmap", ""}},
540         { NFSEXP_KERBEROS, { "kerberos", ""}},
541         { NFSEXP_SUNSECURE, { "sunsecure", ""}},
542         { NFSEXP_CROSSMNT, {"nohide", ""}},
543         { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
544         { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
545 #ifdef MSNFS
546         { NFSEXP_MSNFS, {"msnfs", ""}},
547 #endif
548         { 0, {"", ""}}
549 };
550
551 static int
552 exp_flags(char *buffer, int flag)
553 {
554     int len = 0, first = 0;
555     struct flags *flg = expflags;
556
557     for (;flg->flag;flg++) {
558         int state = (flg->flag & flag)?0:1;
559         if (!flg->flag)
560                 break;
561         if (*flg->name[state]) {
562                 len += sprintf(buffer + len, "%s%s",
563                                first++?",":"", flg->name[state]);
564         }
565     }
566     return len;
567 }
568
569
570
571 /* mangling borrowed from fs/super.c */
572 /* Use octal escapes, like mount does, for embedded spaces etc. */
573 static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' };
574
575 static int
576 mangle(const unsigned char *s, char *buf, int len) {
577         char *sp;
578         int n;
579
580         sp = buf;
581         while(*s && sp-buf < len-3) {
582                 for (n = 0; n < sizeof(need_escaping); n++) {
583                         if (*s == need_escaping[n]) {
584                                 *sp++ = '\\';
585                                 *sp++ = '0' + ((*s & 0300) >> 6);
586                                 *sp++ = '0' + ((*s & 070) >> 3);
587                                 *sp++ = '0' + (*s & 07);
588                                 goto next;
589                         }
590                 }
591                 *sp++ = *s;
592         next:
593                 s++;
594         }
595         return sp - buf;        /* no trailing NUL */
596 }
597
598 #define FREEROOM        ((int)PAGE_SIZE-200-len)
599 #define MANGLE(s)       len += mangle((s), buffer+len, FREEROOM);
600
601 int
602 exp_procfs_exports(char *buffer, char **start, off_t offset,
603                              int length, int *eof, void *data)
604 {
605         struct svc_clnthash     **hp, **head, *tmp;
606         struct svc_client       *clp;
607         svc_export *exp;
608         off_t   pos = 0;
609         off_t   begin = 0;
610         int     len = 0;
611         int     i,j;
612
613         len += sprintf(buffer, "# Version 1.1\n");
614         len += sprintf(buffer+len, "# Path Client(Flags) # IPs\n");
615
616         for (clp = clients; clp; clp = clp->cl_next) {
617                 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
618                         exp = clp->cl_export[i];
619                         while (exp) {
620                                 int first = 0;
621                                 MANGLE(exp->ex_path);
622                                 buffer[len++]='\t';
623                                 MANGLE(clp->cl_ident);
624                                 buffer[len++]='(';
625
626                                 len += exp_flags(buffer+len, exp->ex_flags);
627                                 len += sprintf(buffer+len, ") # ");
628                                 for (j = 0; j < clp->cl_naddr; j++) {
629                                         struct in_addr  addr = clp->cl_addr[j]; 
630
631                                         head = &clnt_hash[CLIENT_HASH(addr.s_addr)];
632                                         for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
633                                                 if (tmp->h_addr.s_addr == addr.s_addr) {
634                                                         if (first++) len += sprintf(buffer+len, "%s", " ");
635                                                         if (tmp->h_client != clp)
636                                                                 len += sprintf(buffer+len, "(");
637                                                         len += sprintf(buffer+len, "%d.%d.%d.%d",
638                                                                         htonl(addr.s_addr) >> 24 & 0xff,
639                                                                         htonl(addr.s_addr) >> 16 & 0xff,
640                                                                         htonl(addr.s_addr) >>  8 & 0xff,
641                                                                         htonl(addr.s_addr) >>  0 & 0xff);
642                                                         if (tmp->h_client != clp)
643                                                           len += sprintf(buffer+len, ")");
644                                                         break;
645                                                 }
646                                         }
647                                 }
648                                 exp = exp->ex_next;
649
650                                 buffer[len++]='\n';
651
652                                 pos=begin+len;
653                                 if(pos<offset) {
654                                         len=0;
655                                         begin=pos;
656                                 }
657                                 if (pos > offset + length)
658                                         goto done;
659                         }
660                 }
661         }
662
663         *eof = 1;
664
665 done:
666         *start = buffer + (offset - begin);
667         len -= (offset - begin);
668         if ( len > length )
669                 len = length;
670         return len;
671 }
672
673 /*
674  * Add or modify a client.
675  * Change requests may involve the list of host addresses. The list of
676  * exports and possibly existing uid maps are left untouched.
677  */
678 int
679 exp_addclient(struct nfsctl_client *ncp)
680 {
681         struct svc_clnthash *   ch[NFSCLNT_ADDRMAX];
682         svc_client *            clp;
683         int                     i, err, change = 0, ilen;
684
685         /* First, consistency check. */
686         err = -EINVAL;
687         if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
688                 goto out;
689         if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
690                 goto out;
691
692         /* Lock the hashtable */
693         if ((err = exp_writelock()) < 0)
694                 goto out;
695
696         /* First check if this is a change request for a client. */
697         for (clp = clients; clp; clp = clp->cl_next)
698                 if (!strcmp(clp->cl_ident, ncp->cl_ident))
699                         break;
700
701         err = -ENOMEM;
702         if (clp) {
703                 change = 1;
704         } else {
705                 if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
706                         goto out_unlock;
707                 memset(clp, 0, sizeof(*clp));
708
709                 dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
710
711                 strcpy(clp->cl_ident, ncp->cl_ident);
712                 clp->cl_idlen = ilen;
713         }
714
715         /* Allocate hash buckets */
716         for (i = 0; i < ncp->cl_naddr; i++) {
717                 ch[i] = kmalloc(sizeof(struct svc_clnthash), GFP_KERNEL);
718                 if (!ch[i]) {
719                         while (i--)
720                                 kfree(ch[i]);
721                         if (!change)
722                                 kfree(clp);
723                         goto out_unlock;
724                 }
725         }
726
727         /* Copy addresses. */
728         for (i = 0; i < ncp->cl_naddr; i++) {
729                 clp->cl_addr[i] = ncp->cl_addrlist[i];
730         }
731         clp->cl_naddr = ncp->cl_naddr;
732
733         /* Remove old client hash entries. */
734         if (change)
735                 exp_unhashclient(clp);
736
737         /* Insert client into hashtable. */
738         for (i = 0; i < ncp->cl_naddr; i++) {
739                 struct in_addr  addr = clp->cl_addr[i];
740                 int             hash;
741
742                 hash = CLIENT_HASH(addr.s_addr);
743                 ch[i]->h_client = clp;
744                 ch[i]->h_addr = addr;
745                 ch[i]->h_next = clnt_hash[hash];
746                 clnt_hash[hash] = ch[i];
747         }
748
749         if (!change) {
750                 clp->cl_next = clients;
751                 clients = clp;
752         }
753         err = 0;
754
755 out_unlock:
756         exp_unlock();
757 out:
758         return err;
759 }
760
761 /*
762  * Delete a client given an identifier.
763  */
764 int
765 exp_delclient(struct nfsctl_client *ncp)
766 {
767         svc_client      **clpp, *clp;
768         int             err;
769
770         err = -EINVAL;
771         if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
772                 goto out;
773
774         /* Lock the hashtable */
775         if ((err = exp_writelock()) < 0)
776                 goto out;
777
778         err = -EINVAL;
779         for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
780                 if (!strcmp(ncp->cl_ident, clp->cl_ident))
781                         break;
782
783         if (clp) {
784                 *clpp = clp->cl_next;
785                 exp_freeclient(clp);
786                 err = 0;
787         }
788
789         exp_unlock();
790 out:
791         return err;
792 }
793
794 /*
795  * Free a client. The caller has already removed it from the client list.
796  */
797 static void
798 exp_freeclient(svc_client *clp)
799 {
800         exp_unhashclient(clp);
801
802         /* umap_free(&(clp->cl_umap)); */
803         exp_unexport_all(clp);
804         nfsd_lockd_unexport(clp);
805         kfree (clp);
806 }
807
808 /*
809  * Remove client from hashtable. We first collect all hashtable
810  * entries and free them in one go.
811  * The hash table must be writelocked by the caller.
812  */
813 static void
814 exp_unhashclient(svc_client *clp)
815 {
816         struct svc_clnthash     **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
817         int                     i, count, err;
818
819 again:
820         err = 0;
821         for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
822                 hpp = clnt_hash + i;
823                 while ((hp = *hpp) && !err) {
824                         if (hp->h_client == clp) {
825                                 *hpp = hp->h_next;
826                                 ch[count++] = hp;
827                                 err = (count >= NFSCLNT_ADDRMAX);
828                         } else {
829                                 hpp = &(hp->h_next);
830                         }
831                 }
832         }
833         if (count != clp->cl_naddr)
834                 printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
835         if (err)
836                 goto again;
837         for (i = 0; i < count; i++)
838                 kfree (ch[i]);
839 }
840
841 /*
842  * Lockd is shutting down and tells us to unregister all clients
843  */
844 void
845 exp_nlmdetach(void)
846 {
847         struct svc_client       *clp;
848
849         for (clp = clients; clp; clp = clp->cl_next)
850                 nfsd_lockd_unexport(clp);
851 }
852
853 /*
854  * Verify that string is non-empty and does not exceed max length.
855  */
856 static int
857 exp_verify_string(char *cp, int max)
858 {
859         int     i;
860
861         for (i = 0; i < max; i++)
862                 if (!cp[i])
863                         return i;
864         cp[i] = 0;
865         printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
866         return 0;
867 }
868
869 /*
870  * Initialize the exports module.
871  */
872 void
873 nfsd_export_init(void)
874 {
875         int             i;
876
877         dprintk("nfsd: initializing export module.\n");
878         if (initialized)
879                 return;
880         for (i = 0; i < CLIENT_HASHMAX; i++)
881                 clnt_hash[i] = NULL;
882         clients = NULL;
883
884         initialized = 1;
885 }
886
887 /*
888  * Shutdown the exports module.
889  */
890 void
891 nfsd_export_shutdown(void)
892 {
893         int     i;
894
895         dprintk("nfsd: shutting down export module.\n");
896         if (!initialized)
897                 return;
898         if (exp_writelock() < 0) {
899                 printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
900                 return;
901         }
902         for (i = 0; i < CLIENT_HASHMAX; i++) {
903                 while (clnt_hash[i])
904                         exp_freeclient(clnt_hash[i]->h_client);
905         }
906         clients = NULL; /* we may be restarted before the module unloads */
907         
908         exp_unlock();
909         dprintk("nfsd: export shutdown complete.\n");
910 }