Import 2.1.81
[davej-history.git] / fs / nfsd / export.c
blob836c1b4d6f538e03c925f5c41f4e6888721c7e61
1 /*
2 * linux/fs/nfsd/export.c
4 * NFS exporting and validation.
6 * We maintain a list of clients, each of which has a list of
7 * exports. To export an fs to a given client, you first have
8 * to create the client entry with NFSCTL_ADDCLIENT, which
9 * creates a client control block and adds it to the hash
10 * table. Then, you call NFSCTL_EXPORT for each fs.
12 * You cannot currently read the export information from the
13 * kernel. It would be nice to have a /proc file though.
15 * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
18 #include <linux/unistd.h>
19 #include <linux/malloc.h>
20 #include <linux/stat.h>
21 #include <linux/in.h>
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>
29 #define NFSDDBG_FACILITY NFSDDBG_EXPORT
30 #define NFSD_PARANOIA 1
32 typedef struct svc_client svc_client;
33 typedef struct svc_export svc_export;
35 static svc_export * exp_find(svc_client *clp, dev_t dev);
36 static svc_export * exp_parent(svc_client *clp, dev_t dev);
37 static void exp_unexport_all(svc_client *clp);
38 static void exp_do_unexport(svc_export *unexp);
39 static svc_client * exp_getclientbyname(char *name);
40 static void exp_freeclient(svc_client *clp);
41 static void exp_unhashclient(svc_client *clp);
42 static int exp_verify_string(char *cp, int max);
44 #define CLIENT_HASHBITS 6
45 #define CLIENT_HASHMAX (1 << CLIENT_HASHBITS)
46 #define CLIENT_HASHMASK (CLIENT_HASHMAX - 1)
47 #define CLIENT_HASH(a) \
48 ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
49 #define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1))
51 struct svc_clnthash {
52 struct svc_clnthash * h_next;
53 struct in_addr h_addr;
54 struct svc_client * h_client;
56 static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX];
57 static svc_client * clients = NULL;
58 static int initialized = 0;
60 static int hash_lock = 0;
61 static int want_lock = 0;
62 static int hash_count = 0;
63 static struct wait_queue * hash_wait = NULL;
65 #define READLOCK 0
66 #define WRITELOCK 1
69 * Find a client's export for a device.
71 static inline svc_export *
72 exp_find(svc_client *clp, dev_t dev)
74 svc_export * exp;
76 exp = clp->cl_export[EXPORT_HASH(dev)];
77 while (exp && exp->ex_dev != dev)
78 exp = exp->ex_next;
79 return exp;
83 * Find the client's export entry matching xdev/xino.
85 svc_export *
86 exp_get(svc_client *clp, dev_t dev, ino_t ino)
88 svc_export * exp;
90 if (!clp)
91 return NULL;
92 exp = exp_find(clp, dev);
93 return (exp && exp->ex_ino == ino)? exp : NULL;
97 * Check whether there are any exports for a device.
99 static int
100 exp_device_in_use(dev_t dev)
102 struct svc_client *clp;
104 for (clp = clients; clp; clp = clp->cl_next) {
105 if (exp_find(clp, dev))
106 return 1;
108 return 0;
112 * Look up the device of the parent fs.
114 static inline int
115 nfsd_parentdev(dev_t *devp)
117 struct super_block *sb;
119 if (!(sb = get_super(*devp)) || !sb->s_root->d_covers)
120 return 0;
121 if (*devp == sb->s_root->d_covers->d_inode->i_dev)
122 return 0;
123 *devp = sb->s_root->d_covers->d_inode->i_dev;
124 return 1;
128 * Find the parent export entry for a given fs. This function is used
129 * only by the export syscall to keep the export tree consistent.
131 static svc_export *
132 exp_parent(svc_client *clp, dev_t dev)
134 svc_export *exp;
135 dev_t xdev = dev;
137 do {
138 exp = exp_find(clp, xdev);
139 if (exp)
140 return exp;
141 } while (nfsd_parentdev(&xdev));
143 return NULL;
147 * Export a file system.
150 exp_export(struct nfsctl_export *nxp)
152 svc_client *clp;
153 svc_export *exp, *parent;
154 svc_export **head;
155 struct dentry *dentry = NULL;
156 struct inode *inode = NULL;
157 int i, err;
158 dev_t dev;
159 ino_t ino;
161 /* Consistency check */
162 if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
163 !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
164 return -EINVAL;
166 dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
167 nxp->ex_client, nxp->ex_path,
168 nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
169 dev = nxp->ex_dev;
170 ino = nxp->ex_ino;
172 /* Try to lock the export table for update */
173 if ((err = exp_writelock()) < 0)
174 goto out;
176 /* Look up client info */
177 err = -EINVAL;
178 if (!(clp = exp_getclientbyname(nxp->ex_client)))
179 goto out_unlock;
182 * If there's already an export for this file, assume this
183 * is just a flag update.
185 if ((exp = exp_find(clp, dev)) != NULL) {
186 /* Ensure there's only one export per FS. */
187 err = -EPERM;
188 if (exp->ex_ino == ino) {
189 exp->ex_flags = nxp->ex_flags;
190 exp->ex_anon_uid = nxp->ex_anon_uid;
191 exp->ex_anon_gid = nxp->ex_anon_gid;
192 err = 0;
194 goto out_unlock;
197 /* Look up the dentry */
198 err = -EINVAL;
199 dentry = lookup_dentry(nxp->ex_path, NULL, 0);
200 if (IS_ERR(dentry))
201 goto out_unlock;
203 err = -ENOENT;
204 inode = dentry->d_inode;
205 if(!inode)
206 goto finish;
207 err = -EINVAL;
208 if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
209 /* I'm just being paranoid... */
210 goto finish;
213 /* We currently export only dirs. */
214 err = -ENOTDIR;
215 if (!S_ISDIR(inode->i_mode))
216 goto finish;
218 /* If this is a sub-export, must be root of FS */
219 err = -EINVAL;
220 if ((parent = exp_parent(clp, dev)) != NULL) {
221 struct super_block *sb = inode->i_sb;
223 if (inode != sb->s_root->d_inode) {
224 #ifdef NFSD_PARANOIA
225 printk("exp_export: sub-export %s not root of device %s\n",
226 nxp->ex_path, kdevname(sb->s_dev));
227 #endif
228 goto finish;
232 err = -ENOMEM;
233 if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
234 goto finish;
235 dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
237 strcpy(exp->ex_path, nxp->ex_path);
238 exp->ex_client = clp;
239 exp->ex_parent = parent;
240 exp->ex_dentry = dentry;
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 dput(dentry);
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 inode *inode;
289 int i;
291 /* Update parent pointers. */
292 clp = unexp->ex_client;
293 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
294 for (exp = clp->cl_export[i]; exp; exp = exp->ex_next)
295 if (exp->ex_parent == unexp)
296 exp->ex_parent = unexp->ex_parent;
300 * Check whether this is the last export for this device,
301 * and if so flush any cached dentries.
303 if (!exp_device_in_use(unexp->ex_dev)) {
304 printk("exp_do_unexport: %s last use, flushing cache\n",
305 kdevname(unexp->ex_dev));
306 nfsd_fh_flush(unexp->ex_dev);
309 dentry = unexp->ex_dentry;
310 inode = dentry->d_inode;
311 if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
312 printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
313 dput(dentry);
315 kfree(unexp);
319 * Revoke all exports for a given client.
320 * This may look very awkward, but we have to do it this way in order
321 * to avoid race conditions (aka mind the parent pointer).
323 static void
324 exp_unexport_all(svc_client *clp)
326 svc_export *exp;
327 int i;
329 dprintk("unexporting all fs's for clnt %p\n", clp);
330 for (i = 0; i < NFSCLNT_EXPMAX; i++) {
331 exp = clp->cl_export[i];
332 clp->cl_export[i] = NULL;
333 while (exp) {
334 svc_export *next = exp->ex_next;
335 exp_do_unexport(exp);
336 exp = next;
342 * unexport syscall.
345 exp_unexport(struct nfsctl_export *nxp)
347 svc_client *clp;
348 svc_export **expp, *exp = NULL;
349 int err;
351 /* Consistency check */
352 if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
353 return -EINVAL;
355 if ((err = exp_writelock()) < 0)
356 goto out;
358 err = -EINVAL;
359 clp = exp_getclientbyname(nxp->ex_client);
360 if (clp) {
361 printk("exp_unexport: found client %s\n", nxp->ex_client);
362 expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev);
363 while ((exp = *expp) != NULL) {
364 if (exp->ex_dev == nxp->ex_dev) {
365 if (exp->ex_ino != nxp->ex_ino) {
366 printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex_ino);
367 break;
369 *expp = exp->ex_next;
370 exp_do_unexport(exp);
371 err = 0;
372 break;
374 expp = &(exp->ex_next);
378 exp_unlock();
379 out:
380 return err;
384 * Obtain the root fh on behalf of a client.
385 * This could be done in user space, but I feel that it adds some safety
386 * since its harder to fool a kernel module than a user space program.
389 exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f)
391 struct svc_export *exp = NULL;
392 struct svc_fh fh;
393 struct dentry *dentry;
394 struct inode *inode;
396 dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
398 if (!(exp = exp_get(clp, dev, ino)))
399 return -EPERM;
401 dentry = exp->ex_dentry;
402 inode = dentry->d_inode;
403 if(!inode) {
404 printk("exp_rootfh: Aieee, NULL d_inode\n");
405 return -EPERM;
407 if(inode->i_dev != dev || inode->i_ino != ino) {
408 printk("exp_rootfh: Aieee, ino/dev mismatch\n");
409 printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n",
410 dev, ino, inode->i_dev, inode->i_ino);
413 dget(dentry);
414 fh_compose(&fh, exp, dentry);
415 memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
416 fh_put(&fh);
418 return 0;
422 * Hashtable locking. Write locks are placed only by user processes
423 * wanting to modify export information.
425 void
426 exp_readlock(void)
428 while (hash_lock || want_lock)
429 sleep_on(&hash_wait);
430 hash_count++;
434 exp_writelock(void)
436 /* fast track */
437 if (!hash_count && !hash_lock) {
438 lock_it:
439 hash_lock = 1;
440 return 0;
443 current->sigpending = 0;
444 want_lock++;
445 while (hash_count || hash_lock) {
446 interruptible_sleep_on(&hash_wait);
447 if (signal_pending(current))
448 break;
450 want_lock--;
452 /* restore the task's signals */
453 spin_lock_irq(&current->sigmask_lock);
454 recalc_sigpending(current);
455 spin_unlock_irq(&current->sigmask_lock);
457 if (!hash_count && !hash_lock)
458 goto lock_it;
459 return -EINTR;
462 void
463 exp_unlock(void)
465 if (!hash_count && !hash_lock)
466 printk(KERN_WARNING "exp_unlock: not locked!\n");
467 if (hash_count)
468 hash_count--;
469 else
470 hash_lock = 0;
471 wake_up(&hash_wait);
475 * Find a valid client given an inet address. We always move the most
476 * recently used client to the front of the hash chain to speed up
477 * future lookups.
478 * Locking against other processes is the responsibility of the caller.
480 struct svc_client *
481 exp_getclient(struct sockaddr_in *sin)
483 struct svc_clnthash **hp, **head, *tmp;
484 unsigned long addr = sin->sin_addr.s_addr;
486 if (!initialized)
487 return NULL;
489 head = &clnt_hash[CLIENT_HASH(addr)];
491 for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
492 if (tmp->h_addr.s_addr == addr) {
493 /* Move client to the front */
494 if (head != hp) {
495 *hp = tmp->h_next;
496 tmp->h_next = *head;
497 *head = tmp;
500 return tmp->h_client;
504 return NULL;
508 * Find a client given its identifier.
510 static svc_client *
511 exp_getclientbyname(char *ident)
513 svc_client * clp;
515 for (clp = clients; clp; clp = clp->cl_next) {
516 if (!strcmp(clp->cl_ident, ident))
517 return clp;
519 return NULL;
523 * Add or modify a client.
524 * Change requests may involve the list of host addresses. The list of
525 * exports and possibly existing uid maps are left untouched.
528 exp_addclient(struct nfsctl_client *ncp)
530 struct svc_clnthash * ch[NFSCLNT_ADDRMAX];
531 svc_client * clp;
532 int i, err, change = 0, ilen;
534 /* First, consistency check. */
535 err = -EINVAL;
536 if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
537 goto out;
538 if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
539 goto out;
541 /* Lock the hashtable */
542 if ((err = exp_writelock()) < 0)
543 goto out;
545 /* First check if this is a change request for a client. */
546 for (clp = clients; clp; clp = clp->cl_next)
547 if (!strcmp(clp->cl_ident, ncp->cl_ident))
548 break;
550 err = -ENOMEM;
551 if (clp) {
552 change = 1;
553 } else {
554 if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
555 goto out_unlock;
556 memset(clp, 0, sizeof(*clp));
558 dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
560 strcpy(clp->cl_ident, ncp->cl_ident);
561 clp->cl_idlen = ilen;
564 /* Allocate hash buckets */
565 for (i = 0; i < ncp->cl_naddr; i++) {
566 ch[i] = kmalloc(sizeof(struct svc_clnthash), GFP_KERNEL);
567 if (!ch[i]) {
568 while (i--)
569 kfree(ch[i]);
570 if (!change)
571 kfree(clp);
572 goto out_unlock;
576 /* Copy addresses. */
577 for (i = 0; i < ncp->cl_naddr; i++) {
578 clp->cl_addr[i] = ncp->cl_addrlist[i];
580 clp->cl_naddr = ncp->cl_naddr;
582 /* Remove old client hash entries. */
583 if (change)
584 exp_unhashclient(clp);
586 /* Insert client into hashtable. */
587 for (i = 0; i < ncp->cl_naddr; i++) {
588 struct in_addr addr = clp->cl_addr[i];
589 int hash;
591 hash = CLIENT_HASH(addr.s_addr);
592 ch[i]->h_client = clp;
593 ch[i]->h_addr = addr;
594 ch[i]->h_next = clnt_hash[hash];
595 clnt_hash[hash] = ch[i];
598 if (!change) {
599 clp->cl_next = clients;
600 clients = clp;
602 err = 0;
604 out_unlock:
605 exp_unlock();
606 out:
607 return err;
611 * Delete a client given an identifier.
614 exp_delclient(struct nfsctl_client *ncp)
616 svc_client **clpp, *clp;
617 int err;
619 if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
620 return -EINVAL;
622 /* Lock the hashtable */
623 if ((err = exp_writelock()) < 0)
624 return err;
626 for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
627 if (!strcmp(ncp->cl_ident, clp->cl_ident))
628 break;
630 if (!clp) {
631 exp_unlock();
632 return -EINVAL;
634 *clpp = clp->cl_next;
635 exp_freeclient(clp);
637 exp_unlock();
638 return 0;
642 * Free a client. The caller has already removed it from the client list.
644 static void
645 exp_freeclient(svc_client *clp)
647 exp_unhashclient(clp);
649 /* umap_free(&(clp->cl_umap)); */
650 exp_unexport_all(clp);
651 nfsd_lockd_unexport(clp);
652 kfree (clp);
656 * Remove client from hashtable. We first collect all hashtable
657 * entries and free them in one go.
658 * The hash table must be writelocked by the caller.
660 static void
661 exp_unhashclient(svc_client *clp)
663 struct svc_clnthash **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
664 int i, count, err;
666 again:
667 err = 0;
668 for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
669 hpp = clnt_hash + i;
670 while ((hp = *hpp) && !err) {
671 if (hp->h_client == clp) {
672 *hpp = hp->h_next;
673 ch[count++] = hp;
674 err = (count >= NFSCLNT_ADDRMAX);
675 } else {
676 hpp = &(hp->h_next);
680 if (count != clp->cl_naddr)
681 printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
682 if (err)
683 goto again;
684 for (i = 0; i < count; i++)
685 kfree (ch[i]);
689 * Lockd is shutting down and tells us to unregister all clients
691 void
692 exp_nlmdetach(void)
694 struct svc_client *clp;
696 for (clp = clients; clp; clp = clp->cl_next)
697 nfsd_lockd_unexport(clp);
701 * Verify that string is non-empty and does not exceed max length.
703 static int
704 exp_verify_string(char *cp, int max)
706 int i;
708 for (i = 0; i < max; i++)
709 if (!cp[i])
710 return i;
711 cp[i] = 0;
712 printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
713 return 0;
717 * Initialize the exports module.
719 void
720 nfsd_export_init(void)
722 int i;
724 dprintk("nfsd: initializing export module.\n");
725 if (initialized)
726 return;
727 for (i = 0; i < CLIENT_HASHMAX; i++)
728 clnt_hash[i] = NULL;
729 clients = NULL;
731 initialized = 1;
735 * Shutdown the exports module.
737 void
738 nfsd_export_shutdown(void)
740 int i;
742 dprintk("nfsd: shutting down export module.\n");
743 if (!initialized)
744 return;
745 if (exp_writelock() < 0) {
746 printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
747 return;
749 for (i = 0; i < CLIENT_HASHMAX; i++) {
750 while (clnt_hash[i])
751 exp_freeclient(clnt_hash[i]->h_client);
753 exp_unlock();
754 dprintk("nfsd: export shutdown complete.\n");