Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / fs / nfsd / export.c
blob2beed5cf91b385d20e1a107a5f6bf92f668ace32
1 #define MSNFS /* HACK HACK */
2 /*
3 * linux/fs/nfsd/export.c
5 * NFS exporting and validation.
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.
14 * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
17 #include <linux/unistd.h>
18 #include <linux/malloc.h>
19 #include <linux/stat.h>
20 #include <linux/in.h>
22 #include <linux/sunrpc/svc.h>
23 #include <linux/nfsd/nfsd.h>
24 #include <linux/nfsd/nfsfh.h>
25 #include <linux/nfsd/syscall.h>
26 #include <linux/lockd/bind.h>
28 #define NFSDDBG_FACILITY NFSDDBG_EXPORT
29 #define NFSD_PARANOIA 1
31 typedef struct svc_client svc_client;
32 typedef struct svc_export svc_export;
34 static svc_export * exp_find(svc_client *clp, kdev_t dev);
35 static svc_export * exp_parent(svc_client *clp, kdev_t dev,
36 struct dentry *dentry);
37 static svc_export * exp_child(svc_client *clp, kdev_t dev,
38 struct dentry *dentry);
39 static void exp_unexport_all(svc_client *clp);
40 static void exp_do_unexport(svc_export *unexp);
41 static svc_client * exp_getclientbyname(char *name);
42 static void exp_freeclient(svc_client *clp);
43 static void exp_unhashclient(svc_client *clp);
44 static int exp_verify_string(char *cp, int max);
46 #define CLIENT_HASHBITS 6
47 #define CLIENT_HASHMAX (1 << CLIENT_HASHBITS)
48 #define CLIENT_HASHMASK (CLIENT_HASHMAX - 1)
49 #define CLIENT_HASH(a) \
50 ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
51 /* XXX: is this adequate for 32bit kdev_t ? */
52 #define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1))
54 struct svc_clnthash {
55 struct svc_clnthash * h_next;
56 struct in_addr h_addr;
57 struct svc_client * h_client;
59 static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX];
60 static svc_client * clients;
61 static int initialized;
63 static int hash_lock;
64 static int want_lock;
65 static int hash_count;
66 static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
70 * Find a client's export for a device.
72 static inline svc_export *
73 exp_find(svc_client *clp, kdev_t dev)
75 svc_export * exp;
77 exp = clp->cl_export[EXPORT_HASH(dev)];
78 while (exp && exp->ex_dev != dev)
79 exp = exp->ex_next;
80 return exp;
84 * Find the client's export entry matching xdev/xino.
86 svc_export *
87 exp_get(svc_client *clp, kdev_t dev, ino_t ino)
89 svc_export * exp;
91 if (!clp)
92 return NULL;
94 exp = clp->cl_export[EXPORT_HASH(dev)];
95 if (exp)
96 do {
97 if (exp->ex_ino == ino && exp->ex_dev == dev)
98 goto out;
99 } while (NULL != (exp = exp->ex_next));
100 exp = NULL;
101 out:
102 return exp;
106 * Find the export entry for a given dentry. <gam3@acm.org>
108 static svc_export *
109 exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
111 svc_export *exp;
113 if (clp == NULL)
114 return NULL;
116 for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next)
117 if (is_subdir(dentry, exp->ex_dentry))
118 break;
119 return exp;
123 * Find the child export entry for a given fs. This function is used
124 * only by the export syscall to keep the export tree consistent.
125 * <gam3@acm.org>
127 static svc_export *
128 exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
130 svc_export *exp;
132 if (clp == NULL)
133 return NULL;
135 for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next) {
136 struct dentry *ndentry = exp->ex_dentry;
137 if (ndentry && is_subdir(ndentry->d_parent, dentry))
138 break;
140 return exp;
144 * Export a file system.
147 exp_export(struct nfsctl_export *nxp)
149 svc_client *clp;
150 svc_export *exp, *parent;
151 svc_export **head;
152 struct nameidata nd;
153 struct inode *inode = NULL;
154 int i, err;
155 kdev_t dev;
156 ino_t ino;
158 /* Consistency check */
159 err = -EINVAL;
160 if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
161 !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
162 goto out;
164 dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
165 nxp->ex_client, nxp->ex_path,
166 nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags);
167 dev = to_kdev_t(nxp->ex_dev);
168 ino = nxp->ex_ino;
170 /* Try to lock the export table for update */
171 if ((err = exp_writelock()) < 0)
172 goto out;
174 /* Look up client info */
175 err = -EINVAL;
176 if (!(clp = exp_getclientbyname(nxp->ex_client)))
177 goto out_unlock;
180 * If there's already an export for this file, assume this
181 * is just a flag update.
183 if ((exp = exp_get(clp, dev, ino)) != NULL) {
184 exp->ex_flags = nxp->ex_flags;
185 exp->ex_anon_uid = nxp->ex_anon_uid;
186 exp->ex_anon_gid = nxp->ex_anon_gid;
187 err = 0;
188 goto out_unlock;
191 /* Look up the dentry */
192 err = 0;
193 if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd))
194 err = path_walk(nxp->ex_path, &nd);
195 if (err)
196 goto out_unlock;
198 inode = nd.dentry->d_inode;
199 err = -EINVAL;
200 if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) {
201 printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n",
202 inode->i_dev, dev);
203 /* I'm just being paranoid... */
204 goto finish;
207 /* We currently export only dirs and regular files.
208 * This is what umountd does.
210 err = -ENOTDIR;
211 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
212 goto finish;
214 err = -EINVAL;
215 if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) ||
216 inode->i_sb->s_op->read_inode == NULL) {
217 dprintk("exp_export: export of invalid fs type.\n");
218 goto finish;
221 if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) {
222 dprintk("exp_export: export not valid (Rule 3).\n");
223 goto finish;
225 /* Is this is a sub-export, must be a proper subset of FS */
226 if ((parent = exp_parent(clp, dev, nd.dentry)) != NULL) {
227 dprintk("exp_export: sub-export not valid (Rule 2).\n");
228 goto finish;
231 err = -ENOMEM;
232 if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
233 goto finish;
234 dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
236 strcpy(exp->ex_path, nxp->ex_path);
237 exp->ex_client = clp;
238 exp->ex_parent = parent;
239 exp->ex_dentry = nd.dentry;
240 exp->ex_mnt = nd.mnt;
241 exp->ex_flags = nxp->ex_flags;
242 exp->ex_dev = dev;
243 exp->ex_ino = ino;
244 exp->ex_anon_uid = nxp->ex_anon_uid;
245 exp->ex_anon_gid = nxp->ex_anon_gid;
247 /* Update parent pointers of all exports */
248 if (parent) {
249 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
250 svc_export *temp = clp->cl_export[i];
252 while (temp) {
253 if (temp->ex_parent == parent)
254 temp->ex_parent = exp;
255 temp = temp->ex_next;
260 head = clp->cl_export + EXPORT_HASH(dev);
261 exp->ex_next = *head;
262 *head = exp;
264 err = 0;
266 /* Unlock hashtable */
267 out_unlock:
268 exp_unlock();
269 out:
270 return err;
272 /* Release the dentry */
273 finish:
274 path_release(&nd);
275 goto out_unlock;
279 * Unexport a file system. The export entry has already
280 * been removed from the client's list of exported fs's.
282 static void
283 exp_do_unexport(svc_export *unexp)
285 svc_export *exp;
286 svc_client *clp;
287 struct dentry *dentry;
288 struct vfsmount *mnt;
289 struct inode *inode;
290 int i;
292 /* Update parent pointers. */
293 clp = unexp->ex_client;
294 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
295 for (exp = clp->cl_export[i]; exp; exp = exp->ex_next)
296 if (exp->ex_parent == unexp)
297 exp->ex_parent = unexp->ex_parent;
300 dentry = unexp->ex_dentry;
301 mnt = unexp->ex_mnt;
302 inode = dentry->d_inode;
303 if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
304 printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
305 dput(dentry);
306 mntput(mnt);
308 kfree(unexp);
312 * Revoke all exports for a given client.
313 * This may look very awkward, but we have to do it this way in order
314 * to avoid race conditions (aka mind the parent pointer).
316 static void
317 exp_unexport_all(svc_client *clp)
319 svc_export *exp;
320 int i;
322 dprintk("unexporting all fs's for clnt %p\n", clp);
323 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
324 exp = clp->cl_export[i];
325 clp->cl_export[i] = NULL;
326 while (exp) {
327 svc_export *next = exp->ex_next;
328 exp_do_unexport(exp);
329 exp = next;
335 * unexport syscall.
338 exp_unexport(struct nfsctl_export *nxp)
340 svc_client *clp;
341 svc_export **expp, *exp = NULL;
342 int err;
344 /* Consistency check */
345 if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
346 return -EINVAL;
348 if ((err = exp_writelock()) < 0)
349 goto out;
351 err = -EINVAL;
352 clp = exp_getclientbyname(nxp->ex_client);
353 if (clp) {
354 expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev);
355 while ((exp = *expp) != NULL) {
356 if (exp->ex_dev == nxp->ex_dev) {
357 if (exp->ex_ino == nxp->ex_ino) {
358 *expp = exp->ex_next;
359 exp_do_unexport(exp);
360 err = 0;
361 break;
364 expp = &(exp->ex_next);
368 exp_unlock();
369 out:
370 return err;
374 * Obtain the root fh on behalf of a client.
375 * This could be done in user space, but I feel that it adds some safety
376 * since its harder to fool a kernel module than a user space program.
379 exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino,
380 char *path, struct knfsd_fh *f, int maxsize)
382 struct svc_export *exp;
383 struct nameidata nd;
384 struct inode *inode;
385 struct svc_fh fh;
386 int err;
388 err = -EPERM;
389 if (path) {
390 if (path_init(path, LOOKUP_POSITIVE, &nd) &&
391 path_walk(path, &nd)) {
392 printk("nfsd: exp_rootfh path not found %s", path);
393 return err;
395 dev = nd.dentry->d_inode->i_dev;
396 ino = nd.dentry->d_inode->i_ino;
398 dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n",
399 path, nd.dentry, clp->cl_ident, dev, (long) ino);
400 exp = exp_parent(clp, dev, nd.dentry);
401 } else {
402 dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n",
403 clp->cl_ident, dev, (long) ino);
404 if ((exp = exp_get(clp, dev, ino))) {
405 nd.mnt = mntget(exp->ex_mnt);
406 nd.dentry = dget(exp->ex_dentry);
409 if (!exp) {
410 dprintk("nfsd: exp_rootfh export not found.\n");
411 goto out;
414 inode = nd.dentry->d_inode;
415 if (!inode) {
416 printk("exp_rootfh: Aieee, NULL d_inode\n");
417 goto out;
419 if (inode->i_dev != dev || inode->i_ino != ino) {
420 printk("exp_rootfh: Aieee, ino/dev mismatch\n");
421 printk("exp_rootfh: arg[dev(%x):ino(%ld)]"
422 " inode[dev(%x):ino(%ld)]\n",
423 dev, (long) ino, inode->i_dev, (long) inode->i_ino);
427 * fh must be initialized before calling fh_compose
429 fh_init(&fh, maxsize);
430 if (fh_compose(&fh, exp, dget(nd.dentry)))
431 err = -EINVAL;
432 else
433 err = 0;
434 memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
435 fh_put(&fh);
437 out:
438 if (path)
439 path_release(&nd);
440 return err;
444 * Hashtable locking. Write locks are placed only by user processes
445 * wanting to modify export information.
447 void
448 exp_readlock(void)
450 while (hash_lock || want_lock)
451 sleep_on(&hash_wait);
452 hash_count++;
456 exp_writelock(void)
458 /* fast track */
459 if (!hash_count && !hash_lock) {
460 lock_it:
461 hash_lock = 1;
462 return 0;
465 current->sigpending = 0;
466 want_lock++;
467 while (hash_count || hash_lock) {
468 interruptible_sleep_on(&hash_wait);
469 if (signal_pending(current))
470 break;
472 want_lock--;
474 /* restore the task's signals */
475 spin_lock_irq(&current->sigmask_lock);
476 recalc_sigpending(current);
477 spin_unlock_irq(&current->sigmask_lock);
479 if (!hash_count && !hash_lock)
480 goto lock_it;
481 return -EINTR;
484 void
485 exp_unlock(void)
487 if (!hash_count && !hash_lock)
488 printk(KERN_WARNING "exp_unlock: not locked!\n");
489 if (hash_count)
490 hash_count--;
491 else
492 hash_lock = 0;
493 wake_up(&hash_wait);
497 * Find a valid client given an inet address. We always move the most
498 * recently used client to the front of the hash chain to speed up
499 * future lookups.
500 * Locking against other processes is the responsibility of the caller.
502 struct svc_client *
503 exp_getclient(struct sockaddr_in *sin)
505 struct svc_clnthash **hp, **head, *tmp;
506 unsigned long addr = sin->sin_addr.s_addr;
508 if (!initialized)
509 return NULL;
511 head = &clnt_hash[CLIENT_HASH(addr)];
513 for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
514 if (tmp->h_addr.s_addr == addr) {
515 /* Move client to the front */
516 if (head != hp) {
517 *hp = tmp->h_next;
518 tmp->h_next = *head;
519 *head = tmp;
522 return tmp->h_client;
526 return NULL;
530 * Find a client given its identifier.
532 static svc_client *
533 exp_getclientbyname(char *ident)
535 svc_client * clp;
537 for (clp = clients; clp; clp = clp->cl_next) {
538 if (!strcmp(clp->cl_ident, ident))
539 return clp;
541 return NULL;
544 struct flags {
545 int flag;
546 char *name[2];
547 } expflags[] = {
548 { NFSEXP_READONLY, {"ro", "rw"}},
549 { NFSEXP_INSECURE_PORT, {"insecure", ""}},
550 { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
551 { NFSEXP_ALLSQUASH, {"all_squash", ""}},
552 { NFSEXP_ASYNC, {"async", ""}},
553 { NFSEXP_GATHERED_WRITES, {"wdelay", ""}},
554 { NFSEXP_UIDMAP, {"uidmap", ""}},
555 { NFSEXP_KERBEROS, { "kerberos", ""}},
556 { NFSEXP_SUNSECURE, { "sunsecure", ""}},
557 { NFSEXP_CROSSMNT, {"nohide", ""}},
558 { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
559 { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
560 #ifdef NSMFS
561 { NFSEXP_MSNFS, {"msnfs", ""}},
562 #endif
563 { 0, {"", ""}}
566 static int
567 exp_flags(char *buffer, int flag)
569 int len = 0, first = 0;
570 struct flags *flg = expflags;
572 for (;flg->flag;flg++) {
573 int state = (flg->flag & flag)?0:1;
574 if (!flg->flag)
575 break;
576 if (*flg->name[state]) {
577 len += sprintf(buffer + len, "%s%s",
578 first++?",":"", flg->name[state]);
581 return len;
585 exp_procfs_exports(char *buffer, char **start, off_t offset,
586 int length, int *eof, void *data)
588 struct svc_clnthash **hp, **head, *tmp;
589 struct svc_client *clp;
590 svc_export *exp;
591 off_t pos = 0;
592 off_t begin = 0;
593 int len = 0;
594 int i,j;
596 len += sprintf(buffer, "# Version 1.0\n");
597 len += sprintf(buffer+len, "# Path Client(Flags) # IPs\n");
599 for (clp = clients; clp; clp = clp->cl_next) {
600 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
601 exp = clp->cl_export[i];
602 while (exp) {
603 int first = 0;
604 len += sprintf(buffer+len, "%s\t", exp->ex_path);
605 len += sprintf(buffer+len, "%s", clp->cl_ident);
606 len += sprintf(buffer+len, "(");
608 len += exp_flags(buffer+len, exp->ex_flags);
609 len += sprintf(buffer+len, ") # ");
610 for (j = 0; j < clp->cl_naddr; j++) {
611 struct in_addr addr = clp->cl_addr[j];
613 head = &clnt_hash[CLIENT_HASH(addr.s_addr)];
614 for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
615 if (tmp->h_addr.s_addr == addr.s_addr) {
616 if (first++) len += sprintf(buffer+len, "%s", " ");
617 if (tmp->h_client != clp)
618 len += sprintf(buffer+len, "(");
619 len += sprintf(buffer+len, "%d.%d.%d.%d",
620 htonl(addr.s_addr) >> 24 & 0xff,
621 htonl(addr.s_addr) >> 16 & 0xff,
622 htonl(addr.s_addr) >> 8 & 0xff,
623 htonl(addr.s_addr) >> 0 & 0xff);
624 if (tmp->h_client != clp)
625 len += sprintf(buffer+len, ")");
626 break;
630 exp = exp->ex_next;
632 buffer[len++]='\n';
634 pos=begin+len;
635 if(pos<offset) {
636 len=0;
637 begin=pos;
639 if (pos > offset + length)
640 goto done;
645 *eof = 1;
647 done:
648 *start = buffer + (offset - begin);
649 len -= (offset - begin);
650 if ( len > length )
651 len = length;
652 return len;
656 * Add or modify a client.
657 * Change requests may involve the list of host addresses. The list of
658 * exports and possibly existing uid maps are left untouched.
661 exp_addclient(struct nfsctl_client *ncp)
663 struct svc_clnthash * ch[NFSCLNT_ADDRMAX];
664 svc_client * clp;
665 int i, err, change = 0, ilen;
667 /* First, consistency check. */
668 err = -EINVAL;
669 if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
670 goto out;
671 if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
672 goto out;
674 /* Lock the hashtable */
675 if ((err = exp_writelock()) < 0)
676 goto out;
678 /* First check if this is a change request for a client. */
679 for (clp = clients; clp; clp = clp->cl_next)
680 if (!strcmp(clp->cl_ident, ncp->cl_ident))
681 break;
683 err = -ENOMEM;
684 if (clp) {
685 change = 1;
686 } else {
687 if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
688 goto out_unlock;
689 memset(clp, 0, sizeof(*clp));
691 dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
693 strcpy(clp->cl_ident, ncp->cl_ident);
694 clp->cl_idlen = ilen;
697 /* Allocate hash buckets */
698 for (i = 0; i < ncp->cl_naddr; i++) {
699 ch[i] = kmalloc(sizeof(struct svc_clnthash), GFP_KERNEL);
700 if (!ch[i]) {
701 while (i--)
702 kfree(ch[i]);
703 if (!change)
704 kfree(clp);
705 goto out_unlock;
709 /* Copy addresses. */
710 for (i = 0; i < ncp->cl_naddr; i++) {
711 clp->cl_addr[i] = ncp->cl_addrlist[i];
713 clp->cl_naddr = ncp->cl_naddr;
715 /* Remove old client hash entries. */
716 if (change)
717 exp_unhashclient(clp);
719 /* Insert client into hashtable. */
720 for (i = 0; i < ncp->cl_naddr; i++) {
721 struct in_addr addr = clp->cl_addr[i];
722 int hash;
724 hash = CLIENT_HASH(addr.s_addr);
725 ch[i]->h_client = clp;
726 ch[i]->h_addr = addr;
727 ch[i]->h_next = clnt_hash[hash];
728 clnt_hash[hash] = ch[i];
731 if (!change) {
732 clp->cl_next = clients;
733 clients = clp;
735 err = 0;
737 out_unlock:
738 exp_unlock();
739 out:
740 return err;
744 * Delete a client given an identifier.
747 exp_delclient(struct nfsctl_client *ncp)
749 svc_client **clpp, *clp;
750 int err;
752 err = -EINVAL;
753 if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
754 goto out;
756 /* Lock the hashtable */
757 if ((err = exp_writelock()) < 0)
758 goto out;
760 err = -EINVAL;
761 for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
762 if (!strcmp(ncp->cl_ident, clp->cl_ident))
763 break;
765 if (clp) {
766 *clpp = clp->cl_next;
767 exp_freeclient(clp);
768 err = 0;
771 exp_unlock();
772 out:
773 return err;
777 * Free a client. The caller has already removed it from the client list.
779 static void
780 exp_freeclient(svc_client *clp)
782 exp_unhashclient(clp);
784 /* umap_free(&(clp->cl_umap)); */
785 exp_unexport_all(clp);
786 nfsd_lockd_unexport(clp);
787 kfree (clp);
791 * Remove client from hashtable. We first collect all hashtable
792 * entries and free them in one go.
793 * The hash table must be writelocked by the caller.
795 static void
796 exp_unhashclient(svc_client *clp)
798 struct svc_clnthash **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
799 int i, count, err;
801 again:
802 err = 0;
803 for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
804 hpp = clnt_hash + i;
805 while ((hp = *hpp) && !err) {
806 if (hp->h_client == clp) {
807 *hpp = hp->h_next;
808 ch[count++] = hp;
809 err = (count >= NFSCLNT_ADDRMAX);
810 } else {
811 hpp = &(hp->h_next);
815 if (count != clp->cl_naddr)
816 printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
817 if (err)
818 goto again;
819 for (i = 0; i < count; i++)
820 kfree (ch[i]);
824 * Lockd is shutting down and tells us to unregister all clients
826 void
827 exp_nlmdetach(void)
829 struct svc_client *clp;
831 for (clp = clients; clp; clp = clp->cl_next)
832 nfsd_lockd_unexport(clp);
836 * Verify that string is non-empty and does not exceed max length.
838 static int
839 exp_verify_string(char *cp, int max)
841 int i;
843 for (i = 0; i < max; i++)
844 if (!cp[i])
845 return i;
846 cp[i] = 0;
847 printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
848 return 0;
852 * Initialize the exports module.
854 void
855 nfsd_export_init(void)
857 int i;
859 dprintk("nfsd: initializing export module.\n");
860 if (initialized)
861 return;
862 for (i = 0; i < CLIENT_HASHMAX; i++)
863 clnt_hash[i] = NULL;
864 clients = NULL;
866 initialized = 1;
870 * Shutdown the exports module.
872 void
873 nfsd_export_shutdown(void)
875 int i;
877 dprintk("nfsd: shutting down export module.\n");
878 if (!initialized)
879 return;
880 if (exp_writelock() < 0) {
881 printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
882 return;
884 for (i = 0; i < CLIENT_HASHMAX; i++) {
885 while (clnt_hash[i])
886 exp_freeclient(clnt_hash[i]->h_client);
888 clients = NULL; /* we may be restarted before the module unloads */
890 exp_unlock();
891 dprintk("nfsd: export shutdown complete.\n");