~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/nfsd/export.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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/malloc.h>
 19 #include <linux/stat.h>
 20 #include <linux/in.h>
 21 
 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>
 27 
 28 #define NFSDDBG_FACILITY        NFSDDBG_EXPORT
 29 #define NFSD_PARANOIA 1
 30 
 31 typedef struct svc_client       svc_client;
 32 typedef struct svc_export       svc_export;
 33 
 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);
 45 
 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))
 53 
 54 struct svc_clnthash {
 55         struct svc_clnthash *   h_next;
 56         struct in_addr          h_addr;
 57         struct svc_client *     h_client;
 58 };
 59 static struct svc_clnthash *    clnt_hash[CLIENT_HASHMAX];
 60 static svc_client *             clients;
 61 static int                      initialized;
 62 
 63 static int                      hash_lock;
 64 static int                      want_lock;
 65 static int                      hash_count;
 66 static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
 67 
 68 
 69 /*
 70  * Find a client's export for a device.
 71  */
 72 static inline svc_export *
 73 exp_find(svc_client *clp, kdev_t dev)
 74 {
 75         svc_export *    exp;
 76 
 77         exp = clp->cl_export[EXPORT_HASH(dev)];
 78         while (exp && exp->ex_dev != dev)
 79                 exp = exp->ex_next;
 80         return exp;
 81 }
 82 
 83 /*
 84  * Find the client's export entry matching xdev/xino.
 85  */
 86 svc_export *
 87 exp_get(svc_client *clp, kdev_t dev, ino_t ino)
 88 {
 89         svc_export *    exp;
 90 
 91         if (!clp)
 92                 return NULL;
 93 
 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;
103 }
104 
105 /*
106  * Find the export entry for a given dentry.  <gam3@acm.org>
107  */
108 static svc_export *
109 exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
110 {
111         svc_export      *exp;
112 
113         if (clp == NULL)
114                 return NULL;
115 
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;
120 }
121 
122 /*
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>
126  */
127 static svc_export *
128 exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
129 {
130         svc_export      *exp;
131 
132         if (clp == NULL)
133                 return NULL;
134 
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;
139         }
140         return exp;
141 }
142 
143 /*
144  * Export a file system.
145  */
146 int
147 exp_export(struct nfsctl_export *nxp)
148 {
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;
157 
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;
163 
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;
169 
170         /* Try to lock the export table for update */
171         if ((err = exp_writelock()) < 0)
172                 goto out;
173 
174         /* Look up client info */
175         err = -EINVAL;
176         if (!(clp = exp_getclientbyname(nxp->ex_client)))
177                 goto out_unlock;
178 
179         /*
180          * If there's already an export for this file, assume this
181          * is just a flag update.
182          */
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;
189         }
190 
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;
197 
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;
205         }
206 
207         /* We currently export only dirs and regular files.
208          * This is what umountd does.
209          */
210         err = -ENOTDIR;
211         if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
212                 goto finish;
213 
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;
219         }
220 
221         if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) {
222                 dprintk("exp_export: export not valid (Rule 3).\n");
223                 goto finish;
224         }
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;
229         }
230 
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);
235 
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;
246 
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];
251 
252                         while (temp) {
253                                 if (temp->ex_parent == parent)
254                                         temp->ex_parent = exp;
255                                 temp = temp->ex_next;
256                         }
257                 }
258         }
259 
260         head = clp->cl_export + EXPORT_HASH(dev);
261         exp->ex_next = *head;
262         *head = exp;
263 
264         err = 0;
265 
266         /* Unlock hashtable */
267 out_unlock:
268         exp_unlock();
269 out:
270         return err;
271 
272         /* Release the dentry */
273 finish:
274         path_release(&nd);
275         goto out_unlock;
276 }
277 
278 /*
279  * Unexport a file system. The export entry has already
280  * been removed from the client's list of exported fs's.
281  */
282 static void
283 exp_do_unexport(svc_export *unexp)
284 {
285         svc_export      *exp;
286         svc_client      *clp;
287         struct dentry   *dentry;
288         struct vfsmount *mnt;
289         struct inode    *inode;
290         int             i;
291 
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;
298         }
299 
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);
307 
308         kfree(unexp);
309 }
310 
311 /*
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).
315  */
316 static void
317 exp_unexport_all(svc_client *clp)
318 {
319         svc_export      *exp;
320         int             i;
321 
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;
330                 }
331         }
332 }
333 
334 /*
335  * unexport syscall.
336  */
337 int
338 exp_unexport(struct nfsctl_export *nxp)
339 {
340         svc_client      *clp;
341         svc_export      **expp, *exp = NULL;
342         int             err;
343 
344         /* Consistency check */
345         if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
346                 return -EINVAL;
347 
348         if ((err = exp_writelock()) < 0)
349                 goto out;
350 
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;
362                                 }
363                         }
364                         expp = &(exp->ex_next);
365                 }
366         }
367 
368         exp_unlock();
369 out:
370         return err;
371 }
372 
373 /*
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.
377  */
378 int
379 exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino,
380            char *path, struct knfsd_fh *f, int maxsize)
381 {
382         struct svc_export       *exp;
383         struct nameidata        nd;
384         struct inode            *inode;
385         struct svc_fh           fh;
386         int                     err;
387 
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;
394                 }
395                 dev = nd.dentry->d_inode->i_dev;
396                 ino = nd.dentry->d_inode->i_ino;
397         
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);
407                 }
408         }
409         if (!exp) {
410                 dprintk("nfsd: exp_rootfh export not found.\n");
411                 goto out;
412         }
413 
414         inode = nd.dentry->d_inode;
415         if (!inode) {
416                 printk("exp_rootfh: Aieee, NULL d_inode\n");
417                 goto out;
418         }
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);
424         }
425 
426         /*
427          * fh must be initialized before calling fh_compose
428          */
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);
436 
437 out:
438         if (path)
439                 path_release(&nd);
440         return err;
441 }
442 
443 /*
444  * Hashtable locking. Write locks are placed only by user processes
445  * wanting to modify export information.
446  */
447 void
448 exp_readlock(void)
449 {
450         while (hash_lock || want_lock)
451                 sleep_on(&hash_wait);
452         hash_count++;
453 }
454 
455 int
456 exp_writelock(void)
457 {
458         /* fast track */
459         if (!hash_count && !hash_lock) {
460         lock_it:
461                 hash_lock = 1;
462                 return 0;
463         }
464 
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;
471         }
472         want_lock--;
473 
474         /* restore the task's signals */
475         spin_lock_irq(&current->sigmask_lock);
476         recalc_sigpending(current);
477         spin_unlock_irq(&current->sigmask_lock);
478 
479         if (!hash_count && !hash_lock)
480                 goto lock_it;
481         return -EINTR;
482 }
483 
484 void
485 exp_unlock(void)
486 {
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);
494 }
495 
496 /*
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.
501  */
502 struct svc_client *
503 exp_getclient(struct sockaddr_in *sin)
504 {
505         struct svc_clnthash     **hp, **head, *tmp;
506         unsigned long           addr = sin->sin_addr.s_addr;
507 
508         if (!initialized)
509                 return NULL;
510 
511         head = &clnt_hash[CLIENT_HASH(addr)];
512 
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;
520                         }
521 
522                         return tmp->h_client;
523                 }
524         }
525 
526         return NULL;
527 }
528 
529 /*
530  * Find a client given its identifier.
531  */
532 static svc_client *
533 exp_getclientbyname(char *ident)
534 {
535         svc_client *    clp;
536 
537         for (clp = clients; clp; clp = clp->cl_next) {
538                 if (!strcmp(clp->cl_ident, ident))
539                         return clp;
540         }
541         return NULL;
542 }
543 
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, {"", ""}}
564 };
565 
566 static int
567 exp_flags(char *buffer, int flag)
568 {
569     int len = 0, first = 0;
570     struct flags *flg = expflags;
571 
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]);
579         }
580     }
581     return len;
582 }
583 
584 int
585 exp_procfs_exports(char *buffer, char **start, off_t offset,
586                              int length, int *eof, void *data)
587 {
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;
595 
596         len += sprintf(buffer, "# Version 1.0\n");
597         len += sprintf(buffer+len, "# Path Client(Flags) # IPs\n");
598 
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, "(");
607 
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]; 
612 
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;
627                                                 }
628                                         }
629                                 }
630                                 exp = exp->ex_next;
631 
632                                 buffer[len++]='\n';
633 
634                                 pos=begin+len;
635                                 if(pos<offset) {
636                                         len=0;
637                                         begin=pos;
638                                 }
639                                 if (pos > offset + length)
640                                         goto done;
641                         }
642                 }
643         }
644 
645         *eof = 1;
646 
647 done:
648         *start = buffer + (offset - begin);
649         len -= (offset - begin);
650         if ( len > length )
651                 len = length;
652         return len;
653 }
654 
655 /*
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.
659  */
660 int
661 exp_addclient(struct nfsctl_client *ncp)
662 {
663         struct svc_clnthash *   ch[NFSCLNT_ADDRMAX];
664         svc_client *            clp;
665         int                     i, err, change = 0, ilen;
666 
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;
673 
674         /* Lock the hashtable */
675         if ((err = exp_writelock()) < 0)
676                 goto out;
677 
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;
682 
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));
690 
691                 dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
692 
693                 strcpy(clp->cl_ident, ncp->cl_ident);
694                 clp->cl_idlen = ilen;
695         }
696 
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;
706                 }
707         }
708 
709         /* Copy addresses. */
710         for (i = 0; i < ncp->cl_naddr; i++) {
711                 clp->cl_addr[i] = ncp->cl_addrlist[i];
712         }
713         clp->cl_naddr = ncp->cl_naddr;
714 
715         /* Remove old client hash entries. */
716         if (change)
717                 exp_unhashclient(clp);
718 
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;
723 
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];
729         }
730 
731         if (!change) {
732                 clp->cl_next = clients;
733                 clients = clp;
734         }
735         err = 0;
736 
737 out_unlock:
738         exp_unlock();
739 out:
740         return err;
741 }
742 
743 /*
744  * Delete a client given an identifier.
745  */
746 int
747 exp_delclient(struct nfsctl_client *ncp)
748 {
749         svc_client      **clpp, *clp;
750         int             err;
751 
752         err = -EINVAL;
753         if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
754                 goto out;
755 
756         /* Lock the hashtable */
757         if ((err = exp_writelock()) < 0)
758                 goto out;
759 
760         err = -EINVAL;
761         for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
762                 if (!strcmp(ncp->cl_ident, clp->cl_ident))
763                         break;
764 
765         if (clp) {
766                 *clpp = clp->cl_next;
767                 exp_freeclient(clp);
768                 err = 0;
769         }
770 
771         exp_unlock();
772 out:
773         return err;
774 }
775 
776 /*
777  * Free a client. The caller has already removed it from the client list.
778  */
779 static void
780 exp_freeclient(svc_client *clp)
781 {
782         exp_unhashclient(clp);
783 
784         /* umap_free(&(clp->cl_umap)); */
785         exp_unexport_all(clp);
786         nfsd_lockd_unexport(clp);
787         kfree (clp);
788 }
789 
790 /*
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.
794  */
795 static void
796 exp_unhashclient(svc_client *clp)
797 {
798         struct svc_clnthash     **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
799         int                     i, count, err;
800 
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);
812                         }
813                 }
814         }
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]);
821 }
822 
823 /*
824  * Lockd is shutting down and tells us to unregister all clients
825  */
826 void
827 exp_nlmdetach(void)
828 {
829         struct svc_client       *clp;
830 
831         for (clp = clients; clp; clp = clp->cl_next)
832                 nfsd_lockd_unexport(clp);
833 }
834 
835 /*
836  * Verify that string is non-empty and does not exceed max length.
837  */
838 static int
839 exp_verify_string(char *cp, int max)
840 {
841         int     i;
842 
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;
849 }
850 
851 /*
852  * Initialize the exports module.
853  */
854 void
855 nfsd_export_init(void)
856 {
857         int             i;
858 
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;
865 
866         initialized = 1;
867 }
868 
869 /*
870  * Shutdown the exports module.
871  */
872 void
873 nfsd_export_shutdown(void)
874 {
875         int     i;
876 
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;
883         }
884         for (i = 0; i < CLIENT_HASHMAX; i++) {
885                 while (clnt_hash[i])
886                         exp_freeclient(clnt_hash[i]->h_client);
887         }
888         clients = NULL; /* we may be restarted before the module unloads */
889         
890         exp_unlock();
891         dprintk("nfsd: export shutdown complete.\n");
892 }
893 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.