The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/nfsd/export.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 2c416325316f0d26e02e33142995e593


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.