diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/Makefile | 2 | ||||
-rw-r--r-- | fs/nfs/client.c | 735 | ||||
-rw-r--r-- | fs/nfs/dir.c | 16 | ||||
-rw-r--r-- | fs/nfs/getroot.c | 306 | ||||
-rw-r--r-- | fs/nfs/idmap.c | 3 | ||||
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | fs/nfs/internal.h | 82 | ||||
-rw-r--r-- | fs/nfs/namespace.c | 25 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 6 | ||||
-rw-r--r-- | fs/nfs/nfs4namespace.c | 110 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 59 | ||||
-rw-r--r-- | fs/nfs/nfs4renewd.c | 13 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 18 | ||||
-rw-r--r-- | fs/nfs/read.c | 2 | ||||
-rw-r--r-- | fs/nfs/super.c | 1207 | ||||
-rw-r--r-- | fs/nfs/write.c | 2 |
17 files changed, 1646 insertions, 944 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 3b993a6f8163..f4580b44eef4 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_NFS_FS) += nfs.o | 5 | obj-$(CONFIG_NFS_FS) += nfs.o |
6 | 6 | ||
7 | nfs-y := client.o dir.o file.o inode.o super.o nfs2xdr.o \ | 7 | nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ |
8 | pagelist.o proc.o read.o symlink.o unlink.o \ | 8 | pagelist.o proc.o read.o symlink.o unlink.o \ |
9 | write.o namespace.o | 9 | write.o namespace.o |
10 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o | 10 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o |
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c08cab935ad5..dafba608c0a0 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -48,6 +48,7 @@ | |||
48 | 48 | ||
49 | static DEFINE_SPINLOCK(nfs_client_lock); | 49 | static DEFINE_SPINLOCK(nfs_client_lock); |
50 | static LIST_HEAD(nfs_client_list); | 50 | static LIST_HEAD(nfs_client_list); |
51 | static LIST_HEAD(nfs_volume_list); | ||
51 | static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); | 52 | static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); |
52 | 53 | ||
53 | /* | 54 | /* |
@@ -268,9 +269,9 @@ struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversio | |||
268 | * Look up a client by IP address and protocol version | 269 | * Look up a client by IP address and protocol version |
269 | * - creates a new record if one doesn't yet exist | 270 | * - creates a new record if one doesn't yet exist |
270 | */ | 271 | */ |
271 | struct nfs_client *nfs_get_client(const char *hostname, | 272 | static struct nfs_client *nfs_get_client(const char *hostname, |
272 | const struct sockaddr_in *addr, | 273 | const struct sockaddr_in *addr, |
273 | int nfsversion) | 274 | int nfsversion) |
274 | { | 275 | { |
275 | struct nfs_client *clp, *new = NULL; | 276 | struct nfs_client *clp, *new = NULL; |
276 | int error; | 277 | int error; |
@@ -340,6 +341,8 @@ found_client: | |||
340 | return ERR_PTR(error); | 341 | return ERR_PTR(error); |
341 | } | 342 | } |
342 | 343 | ||
344 | BUG_ON(clp->cl_cons_state != NFS_CS_READY); | ||
345 | |||
343 | dprintk("--> nfs_get_client() = %p [share]\n", clp); | 346 | dprintk("--> nfs_get_client() = %p [share]\n", clp); |
344 | return clp; | 347 | return clp; |
345 | } | 348 | } |
@@ -347,7 +350,7 @@ found_client: | |||
347 | /* | 350 | /* |
348 | * Mark a server as ready or failed | 351 | * Mark a server as ready or failed |
349 | */ | 352 | */ |
350 | void nfs_mark_client_ready(struct nfs_client *clp, int state) | 353 | static void nfs_mark_client_ready(struct nfs_client *clp, int state) |
351 | { | 354 | { |
352 | clp->cl_cons_state = state; | 355 | clp->cl_cons_state = state; |
353 | wake_up_all(&nfs_client_active_wq); | 356 | wake_up_all(&nfs_client_active_wq); |
@@ -389,10 +392,10 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, | |||
389 | /* | 392 | /* |
390 | * Create an RPC client handle | 393 | * Create an RPC client handle |
391 | */ | 394 | */ |
392 | int nfs_create_rpc_client(struct nfs_client *clp, int proto, | 395 | static int nfs_create_rpc_client(struct nfs_client *clp, int proto, |
393 | unsigned int timeo, | 396 | unsigned int timeo, |
394 | unsigned int retrans, | 397 | unsigned int retrans, |
395 | rpc_authflavor_t flavor) | 398 | rpc_authflavor_t flavor) |
396 | { | 399 | { |
397 | struct rpc_timeout timeparms; | 400 | struct rpc_timeout timeparms; |
398 | struct rpc_xprt *xprt = NULL; | 401 | struct rpc_xprt *xprt = NULL; |
@@ -429,3 +432,719 @@ int nfs_create_rpc_client(struct nfs_client *clp, int proto, | |||
429 | clp->cl_rpcclient = clnt; | 432 | clp->cl_rpcclient = clnt; |
430 | return 0; | 433 | return 0; |
431 | } | 434 | } |
435 | |||
436 | /* | ||
437 | * Version 2 or 3 client destruction | ||
438 | */ | ||
439 | static void nfs_destroy_server(struct nfs_server *server) | ||
440 | { | ||
441 | if (!IS_ERR(server->client_acl)) | ||
442 | rpc_shutdown_client(server->client_acl); | ||
443 | |||
444 | if (!(server->flags & NFS_MOUNT_NONLM)) | ||
445 | lockd_down(); /* release rpc.lockd */ | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * Version 2 or 3 lockd setup | ||
450 | */ | ||
451 | static int nfs_start_lockd(struct nfs_server *server) | ||
452 | { | ||
453 | int error = 0; | ||
454 | |||
455 | if (server->nfs_client->cl_nfsversion > 3) | ||
456 | goto out; | ||
457 | if (server->flags & NFS_MOUNT_NONLM) | ||
458 | goto out; | ||
459 | error = lockd_up(); | ||
460 | if (error < 0) | ||
461 | server->flags |= NFS_MOUNT_NONLM; | ||
462 | else | ||
463 | server->destroy = nfs_destroy_server; | ||
464 | out: | ||
465 | return error; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * Initialise an NFSv3 ACL client connection | ||
470 | */ | ||
471 | #ifdef CONFIG_NFS_V3_ACL | ||
472 | static void nfs_init_server_aclclient(struct nfs_server *server) | ||
473 | { | ||
474 | if (server->nfs_client->cl_nfsversion != 3) | ||
475 | goto out_noacl; | ||
476 | if (server->flags & NFS_MOUNT_NOACL) | ||
477 | goto out_noacl; | ||
478 | |||
479 | server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); | ||
480 | if (IS_ERR(server->client_acl)) | ||
481 | goto out_noacl; | ||
482 | |||
483 | /* No errors! Assume that Sun nfsacls are supported */ | ||
484 | server->caps |= NFS_CAP_ACLS; | ||
485 | return; | ||
486 | |||
487 | out_noacl: | ||
488 | server->caps &= ~NFS_CAP_ACLS; | ||
489 | } | ||
490 | #else | ||
491 | static inline void nfs_init_server_aclclient(struct nfs_server *server) | ||
492 | { | ||
493 | server->flags &= ~NFS_MOUNT_NOACL; | ||
494 | server->caps &= ~NFS_CAP_ACLS; | ||
495 | } | ||
496 | #endif | ||
497 | |||
498 | /* | ||
499 | * Create a general RPC client | ||
500 | */ | ||
501 | static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t pseudoflavour) | ||
502 | { | ||
503 | struct nfs_client *clp = server->nfs_client; | ||
504 | |||
505 | server->client = rpc_clone_client(clp->cl_rpcclient); | ||
506 | if (IS_ERR(server->client)) { | ||
507 | dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__); | ||
508 | return PTR_ERR(server->client); | ||
509 | } | ||
510 | |||
511 | if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { | ||
512 | struct rpc_auth *auth; | ||
513 | |||
514 | auth = rpcauth_create(pseudoflavour, server->client); | ||
515 | if (IS_ERR(auth)) { | ||
516 | dprintk("%s: couldn't create credcache!\n", __FUNCTION__); | ||
517 | return PTR_ERR(auth); | ||
518 | } | ||
519 | } | ||
520 | server->client->cl_softrtry = 0; | ||
521 | if (server->flags & NFS_MOUNT_SOFT) | ||
522 | server->client->cl_softrtry = 1; | ||
523 | |||
524 | server->client->cl_intr = 0; | ||
525 | if (server->flags & NFS4_MOUNT_INTR) | ||
526 | server->client->cl_intr = 1; | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Initialise an NFS2 or NFS3 client | ||
533 | */ | ||
534 | static int nfs_init_client(struct nfs_client *clp, const struct nfs_mount_data *data) | ||
535 | { | ||
536 | int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; | ||
537 | int error; | ||
538 | |||
539 | if (clp->cl_cons_state == NFS_CS_READY) { | ||
540 | /* the client is already initialised */ | ||
541 | dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | /* Check NFS protocol revision and initialize RPC op vector */ | ||
546 | clp->rpc_ops = &nfs_v2_clientops; | ||
547 | #ifdef CONFIG_NFS_V3 | ||
548 | if (clp->cl_nfsversion == 3) | ||
549 | clp->rpc_ops = &nfs_v3_clientops; | ||
550 | #endif | ||
551 | /* | ||
552 | * Create a client RPC handle for doing FSSTAT with UNIX auth only | ||
553 | * - RFC 2623, sec 2.3.2 | ||
554 | */ | ||
555 | error = nfs_create_rpc_client(clp, proto, data->timeo, data->retrans, | ||
556 | RPC_AUTH_UNIX); | ||
557 | if (error < 0) | ||
558 | goto error; | ||
559 | nfs_mark_client_ready(clp, NFS_CS_READY); | ||
560 | return 0; | ||
561 | |||
562 | error: | ||
563 | nfs_mark_client_ready(clp, error); | ||
564 | dprintk("<-- nfs_init_client() = xerror %d\n", error); | ||
565 | return error; | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Create a version 2 or 3 client | ||
570 | */ | ||
571 | static int nfs_init_server(struct nfs_server *server, const struct nfs_mount_data *data) | ||
572 | { | ||
573 | struct nfs_client *clp; | ||
574 | int error, nfsvers = 2; | ||
575 | |||
576 | dprintk("--> nfs_init_server()\n"); | ||
577 | |||
578 | #ifdef CONFIG_NFS_V3 | ||
579 | if (data->flags & NFS_MOUNT_VER3) | ||
580 | nfsvers = 3; | ||
581 | #endif | ||
582 | |||
583 | /* Allocate or find a client reference we can use */ | ||
584 | clp = nfs_get_client(data->hostname, &data->addr, nfsvers); | ||
585 | if (IS_ERR(clp)) { | ||
586 | dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); | ||
587 | return PTR_ERR(clp); | ||
588 | } | ||
589 | |||
590 | error = nfs_init_client(clp, data); | ||
591 | if (error < 0) | ||
592 | goto error; | ||
593 | |||
594 | server->nfs_client = clp; | ||
595 | |||
596 | /* Initialise the client representation from the mount data */ | ||
597 | server->flags = data->flags & NFS_MOUNT_FLAGMASK; | ||
598 | |||
599 | if (data->rsize) | ||
600 | server->rsize = nfs_block_size(data->rsize, NULL); | ||
601 | if (data->wsize) | ||
602 | server->wsize = nfs_block_size(data->wsize, NULL); | ||
603 | |||
604 | server->acregmin = data->acregmin * HZ; | ||
605 | server->acregmax = data->acregmax * HZ; | ||
606 | server->acdirmin = data->acdirmin * HZ; | ||
607 | server->acdirmax = data->acdirmax * HZ; | ||
608 | |||
609 | /* Start lockd here, before we might error out */ | ||
610 | error = nfs_start_lockd(server); | ||
611 | if (error < 0) | ||
612 | goto error; | ||
613 | |||
614 | error = nfs_init_server_rpcclient(server, data->pseudoflavor); | ||
615 | if (error < 0) | ||
616 | goto error; | ||
617 | |||
618 | server->namelen = data->namlen; | ||
619 | /* Create a client RPC handle for the NFSv3 ACL management interface */ | ||
620 | nfs_init_server_aclclient(server); | ||
621 | if (clp->cl_nfsversion == 3) { | ||
622 | if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) | ||
623 | server->namelen = NFS3_MAXNAMLEN; | ||
624 | server->caps |= NFS_CAP_READDIRPLUS; | ||
625 | } else { | ||
626 | if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) | ||
627 | server->namelen = NFS2_MAXNAMLEN; | ||
628 | } | ||
629 | |||
630 | dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); | ||
631 | return 0; | ||
632 | |||
633 | error: | ||
634 | server->nfs_client = NULL; | ||
635 | nfs_put_client(clp); | ||
636 | dprintk("<-- nfs_init_server() = xerror %d\n", error); | ||
637 | return error; | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * Load up the server record from information gained in an fsinfo record | ||
642 | */ | ||
643 | static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) | ||
644 | { | ||
645 | unsigned long max_rpc_payload; | ||
646 | |||
647 | /* Work out a lot of parameters */ | ||
648 | if (server->rsize == 0) | ||
649 | server->rsize = nfs_block_size(fsinfo->rtpref, NULL); | ||
650 | if (server->wsize == 0) | ||
651 | server->wsize = nfs_block_size(fsinfo->wtpref, NULL); | ||
652 | |||
653 | if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) | ||
654 | server->rsize = nfs_block_size(fsinfo->rtmax, NULL); | ||
655 | if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) | ||
656 | server->wsize = nfs_block_size(fsinfo->wtmax, NULL); | ||
657 | |||
658 | max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); | ||
659 | if (server->rsize > max_rpc_payload) | ||
660 | server->rsize = max_rpc_payload; | ||
661 | if (server->rsize > NFS_MAX_FILE_IO_SIZE) | ||
662 | server->rsize = NFS_MAX_FILE_IO_SIZE; | ||
663 | server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
664 | server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; | ||
665 | |||
666 | if (server->wsize > max_rpc_payload) | ||
667 | server->wsize = max_rpc_payload; | ||
668 | if (server->wsize > NFS_MAX_FILE_IO_SIZE) | ||
669 | server->wsize = NFS_MAX_FILE_IO_SIZE; | ||
670 | server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
671 | server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); | ||
672 | |||
673 | server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); | ||
674 | if (server->dtsize > PAGE_CACHE_SIZE) | ||
675 | server->dtsize = PAGE_CACHE_SIZE; | ||
676 | if (server->dtsize > server->rsize) | ||
677 | server->dtsize = server->rsize; | ||
678 | |||
679 | if (server->flags & NFS_MOUNT_NOAC) { | ||
680 | server->acregmin = server->acregmax = 0; | ||
681 | server->acdirmin = server->acdirmax = 0; | ||
682 | } | ||
683 | |||
684 | server->maxfilesize = fsinfo->maxfilesize; | ||
685 | |||
686 | /* We're airborne Set socket buffersize */ | ||
687 | rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | * Probe filesystem information, including the FSID on v2/v3 | ||
692 | */ | ||
693 | static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) | ||
694 | { | ||
695 | struct nfs_fsinfo fsinfo; | ||
696 | struct nfs_client *clp = server->nfs_client; | ||
697 | int error; | ||
698 | |||
699 | dprintk("--> nfs_probe_fsinfo()\n"); | ||
700 | |||
701 | if (clp->rpc_ops->set_capabilities != NULL) { | ||
702 | error = clp->rpc_ops->set_capabilities(server, mntfh); | ||
703 | if (error < 0) | ||
704 | goto out_error; | ||
705 | } | ||
706 | |||
707 | fsinfo.fattr = fattr; | ||
708 | nfs_fattr_init(fattr); | ||
709 | error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); | ||
710 | if (error < 0) | ||
711 | goto out_error; | ||
712 | |||
713 | nfs_server_set_fsinfo(server, &fsinfo); | ||
714 | |||
715 | /* Get some general file system info */ | ||
716 | if (server->namelen == 0) { | ||
717 | struct nfs_pathconf pathinfo; | ||
718 | |||
719 | pathinfo.fattr = fattr; | ||
720 | nfs_fattr_init(fattr); | ||
721 | |||
722 | if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) | ||
723 | server->namelen = pathinfo.max_namelen; | ||
724 | } | ||
725 | |||
726 | dprintk("<-- nfs_probe_fsinfo() = 0\n"); | ||
727 | return 0; | ||
728 | |||
729 | out_error: | ||
730 | dprintk("nfs_probe_fsinfo: error = %d\n", -error); | ||
731 | return error; | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Copy useful information when duplicating a server record | ||
736 | */ | ||
737 | static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) | ||
738 | { | ||
739 | target->flags = source->flags; | ||
740 | target->acregmin = source->acregmin; | ||
741 | target->acregmax = source->acregmax; | ||
742 | target->acdirmin = source->acdirmin; | ||
743 | target->acdirmax = source->acdirmax; | ||
744 | target->caps = source->caps; | ||
745 | } | ||
746 | |||
747 | /* | ||
748 | * Allocate and initialise a server record | ||
749 | */ | ||
750 | static struct nfs_server *nfs_alloc_server(void) | ||
751 | { | ||
752 | struct nfs_server *server; | ||
753 | |||
754 | server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); | ||
755 | if (!server) | ||
756 | return NULL; | ||
757 | |||
758 | server->client = server->client_acl = ERR_PTR(-EINVAL); | ||
759 | |||
760 | /* Zero out the NFS state stuff */ | ||
761 | INIT_LIST_HEAD(&server->client_link); | ||
762 | INIT_LIST_HEAD(&server->master_link); | ||
763 | |||
764 | server->io_stats = nfs_alloc_iostats(); | ||
765 | if (!server->io_stats) { | ||
766 | kfree(server); | ||
767 | return NULL; | ||
768 | } | ||
769 | |||
770 | return server; | ||
771 | } | ||
772 | |||
773 | /* | ||
774 | * Free up a server record | ||
775 | */ | ||
776 | void nfs_free_server(struct nfs_server *server) | ||
777 | { | ||
778 | dprintk("--> nfs_free_server()\n"); | ||
779 | |||
780 | spin_lock(&nfs_client_lock); | ||
781 | list_del(&server->client_link); | ||
782 | list_del(&server->master_link); | ||
783 | spin_unlock(&nfs_client_lock); | ||
784 | |||
785 | if (server->destroy != NULL) | ||
786 | server->destroy(server); | ||
787 | if (!IS_ERR(server->client)) | ||
788 | rpc_shutdown_client(server->client); | ||
789 | |||
790 | nfs_put_client(server->nfs_client); | ||
791 | |||
792 | nfs_free_iostats(server->io_stats); | ||
793 | kfree(server); | ||
794 | nfs_release_automount_timer(); | ||
795 | dprintk("<-- nfs_free_server()\n"); | ||
796 | } | ||
797 | |||
798 | /* | ||
799 | * Create a version 2 or 3 volume record | ||
800 | * - keyed on server and FSID | ||
801 | */ | ||
802 | struct nfs_server *nfs_create_server(const struct nfs_mount_data *data, | ||
803 | struct nfs_fh *mntfh) | ||
804 | { | ||
805 | struct nfs_server *server; | ||
806 | struct nfs_fattr fattr; | ||
807 | int error; | ||
808 | |||
809 | server = nfs_alloc_server(); | ||
810 | if (!server) | ||
811 | return ERR_PTR(-ENOMEM); | ||
812 | |||
813 | /* Get a client representation */ | ||
814 | error = nfs_init_server(server, data); | ||
815 | if (error < 0) | ||
816 | goto error; | ||
817 | |||
818 | BUG_ON(!server->nfs_client); | ||
819 | BUG_ON(!server->nfs_client->rpc_ops); | ||
820 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | ||
821 | |||
822 | /* Probe the root fh to retrieve its FSID */ | ||
823 | error = nfs_probe_fsinfo(server, mntfh, &fattr); | ||
824 | if (error < 0) | ||
825 | goto error; | ||
826 | if (!(fattr.valid & NFS_ATTR_FATTR)) { | ||
827 | error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); | ||
828 | if (error < 0) { | ||
829 | dprintk("nfs_create_server: getattr error = %d\n", -error); | ||
830 | goto error; | ||
831 | } | ||
832 | } | ||
833 | memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); | ||
834 | |||
835 | dprintk("Server FSID: %llx:%llx\n", server->fsid.major, server->fsid.minor); | ||
836 | |||
837 | BUG_ON(!server->nfs_client); | ||
838 | BUG_ON(!server->nfs_client->rpc_ops); | ||
839 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | ||
840 | |||
841 | spin_lock(&nfs_client_lock); | ||
842 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
843 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
844 | spin_unlock(&nfs_client_lock); | ||
845 | |||
846 | server->mount_time = jiffies; | ||
847 | return server; | ||
848 | |||
849 | error: | ||
850 | nfs_free_server(server); | ||
851 | return ERR_PTR(error); | ||
852 | } | ||
853 | |||
854 | #ifdef CONFIG_NFS_V4 | ||
855 | /* | ||
856 | * Initialise an NFS4 client record | ||
857 | */ | ||
858 | static int nfs4_init_client(struct nfs_client *clp, | ||
859 | int proto, int timeo, int retrans, | ||
860 | rpc_authflavor_t authflavour) | ||
861 | { | ||
862 | int error; | ||
863 | |||
864 | if (clp->cl_cons_state == NFS_CS_READY) { | ||
865 | /* the client is initialised already */ | ||
866 | dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | /* Check NFS protocol revision and initialize RPC op vector */ | ||
871 | clp->rpc_ops = &nfs_v4_clientops; | ||
872 | |||
873 | error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour); | ||
874 | if (error < 0) | ||
875 | goto error; | ||
876 | |||
877 | error = nfs_idmap_new(clp); | ||
878 | if (error < 0) { | ||
879 | dprintk("%s: failed to create idmapper. Error = %d\n", | ||
880 | __FUNCTION__, error); | ||
881 | __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); | ||
882 | goto error; | ||
883 | } | ||
884 | |||
885 | nfs_mark_client_ready(clp, NFS_CS_READY); | ||
886 | return 0; | ||
887 | |||
888 | error: | ||
889 | nfs_mark_client_ready(clp, error); | ||
890 | dprintk("<-- nfs4_init_client() = xerror %d\n", error); | ||
891 | return error; | ||
892 | } | ||
893 | |||
894 | /* | ||
895 | * Set up an NFS4 client | ||
896 | */ | ||
897 | static int nfs4_set_client(struct nfs_server *server, | ||
898 | const char *hostname, const struct sockaddr_in *addr, | ||
899 | rpc_authflavor_t authflavour, | ||
900 | int proto, int timeo, int retrans) | ||
901 | { | ||
902 | struct nfs_client *clp; | ||
903 | int error; | ||
904 | |||
905 | dprintk("--> nfs4_set_client()\n"); | ||
906 | |||
907 | /* Allocate or find a client reference we can use */ | ||
908 | clp = nfs_get_client(hostname, addr, 4); | ||
909 | if (IS_ERR(clp)) { | ||
910 | error = PTR_ERR(clp); | ||
911 | goto error; | ||
912 | } | ||
913 | error = nfs4_init_client(clp, proto, timeo, retrans, authflavour); | ||
914 | if (error < 0) | ||
915 | goto error_put; | ||
916 | |||
917 | server->nfs_client = clp; | ||
918 | dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); | ||
919 | return 0; | ||
920 | |||
921 | error_put: | ||
922 | nfs_put_client(clp); | ||
923 | error: | ||
924 | dprintk("<-- nfs4_set_client() = xerror %d\n", error); | ||
925 | return error; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * Create a version 4 volume record | ||
930 | */ | ||
931 | static int nfs4_init_server(struct nfs_server *server, | ||
932 | const struct nfs4_mount_data *data, rpc_authflavor_t authflavour) | ||
933 | { | ||
934 | int error; | ||
935 | |||
936 | dprintk("--> nfs4_init_server()\n"); | ||
937 | |||
938 | /* Initialise the client representation from the mount data */ | ||
939 | server->flags = data->flags & NFS_MOUNT_FLAGMASK; | ||
940 | server->caps |= NFS_CAP_ATOMIC_OPEN; | ||
941 | |||
942 | if (data->rsize) | ||
943 | server->rsize = nfs_block_size(data->rsize, NULL); | ||
944 | if (data->wsize) | ||
945 | server->wsize = nfs_block_size(data->wsize, NULL); | ||
946 | |||
947 | server->acregmin = data->acregmin * HZ; | ||
948 | server->acregmax = data->acregmax * HZ; | ||
949 | server->acdirmin = data->acdirmin * HZ; | ||
950 | server->acdirmax = data->acdirmax * HZ; | ||
951 | |||
952 | error = nfs_init_server_rpcclient(server, authflavour); | ||
953 | |||
954 | /* Done */ | ||
955 | dprintk("<-- nfs4_init_server() = %d\n", error); | ||
956 | return error; | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | * Create a version 4 volume record | ||
961 | * - keyed on server and FSID | ||
962 | */ | ||
963 | struct nfs_server *nfs4_create_server(const struct nfs4_mount_data *data, | ||
964 | const char *hostname, | ||
965 | const struct sockaddr_in *addr, | ||
966 | const char *mntpath, | ||
967 | const char *ip_addr, | ||
968 | rpc_authflavor_t authflavour, | ||
969 | struct nfs_fh *mntfh) | ||
970 | { | ||
971 | struct nfs_fattr fattr; | ||
972 | struct nfs_server *server; | ||
973 | int error; | ||
974 | |||
975 | dprintk("--> nfs4_create_server()\n"); | ||
976 | |||
977 | server = nfs_alloc_server(); | ||
978 | if (!server) | ||
979 | return ERR_PTR(-ENOMEM); | ||
980 | |||
981 | /* Get a client record */ | ||
982 | error = nfs4_set_client(server, hostname, addr, authflavour, | ||
983 | data->proto, data->timeo, data->retrans); | ||
984 | if (error < 0) | ||
985 | goto error; | ||
986 | |||
987 | /* set up the general RPC client */ | ||
988 | error = nfs4_init_server(server, data, authflavour); | ||
989 | if (error < 0) | ||
990 | goto error; | ||
991 | |||
992 | BUG_ON(!server->nfs_client); | ||
993 | BUG_ON(!server->nfs_client->rpc_ops); | ||
994 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | ||
995 | |||
996 | /* Probe the root fh to retrieve its FSID */ | ||
997 | error = nfs4_path_walk(server, mntfh, mntpath); | ||
998 | if (error < 0) | ||
999 | goto error; | ||
1000 | |||
1001 | dprintk("Server FSID: %llx:%llx\n", server->fsid.major, server->fsid.minor); | ||
1002 | dprintk("Mount FH: %d\n", mntfh->size); | ||
1003 | |||
1004 | error = nfs_probe_fsinfo(server, mntfh, &fattr); | ||
1005 | if (error < 0) | ||
1006 | goto error; | ||
1007 | |||
1008 | BUG_ON(!server->nfs_client); | ||
1009 | BUG_ON(!server->nfs_client->rpc_ops); | ||
1010 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | ||
1011 | |||
1012 | spin_lock(&nfs_client_lock); | ||
1013 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1014 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1015 | spin_unlock(&nfs_client_lock); | ||
1016 | |||
1017 | server->mount_time = jiffies; | ||
1018 | dprintk("<-- nfs4_create_server() = %p\n", server); | ||
1019 | return server; | ||
1020 | |||
1021 | error: | ||
1022 | nfs_free_server(server); | ||
1023 | dprintk("<-- nfs4_create_server() = error %d\n", error); | ||
1024 | return ERR_PTR(error); | ||
1025 | } | ||
1026 | |||
1027 | /* | ||
1028 | * Create an NFS4 referral server record | ||
1029 | */ | ||
1030 | struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, | ||
1031 | struct nfs_fh *fh) | ||
1032 | { | ||
1033 | struct nfs_client *parent_client; | ||
1034 | struct nfs_server *server, *parent_server; | ||
1035 | struct nfs_fattr fattr; | ||
1036 | int error; | ||
1037 | |||
1038 | dprintk("--> nfs4_create_referral_server()\n"); | ||
1039 | |||
1040 | server = nfs_alloc_server(); | ||
1041 | if (!server) | ||
1042 | return ERR_PTR(-ENOMEM); | ||
1043 | |||
1044 | parent_server = NFS_SB(data->sb); | ||
1045 | parent_client = parent_server->nfs_client; | ||
1046 | |||
1047 | /* Get a client representation. | ||
1048 | * Note: NFSv4 always uses TCP, */ | ||
1049 | error = nfs4_set_client(server, data->hostname, data->addr, | ||
1050 | data->authflavor, | ||
1051 | parent_server->client->cl_xprt->prot, | ||
1052 | parent_client->retrans_timeo, | ||
1053 | parent_client->retrans_count); | ||
1054 | |||
1055 | /* Initialise the client representation from the parent server */ | ||
1056 | nfs_server_copy_userdata(server, parent_server); | ||
1057 | server->caps |= NFS_CAP_ATOMIC_OPEN; | ||
1058 | |||
1059 | error = nfs_init_server_rpcclient(server, data->authflavor); | ||
1060 | if (error < 0) | ||
1061 | goto error; | ||
1062 | |||
1063 | BUG_ON(!server->nfs_client); | ||
1064 | BUG_ON(!server->nfs_client->rpc_ops); | ||
1065 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | ||
1066 | |||
1067 | /* probe the filesystem info for this server filesystem */ | ||
1068 | error = nfs_probe_fsinfo(server, fh, &fattr); | ||
1069 | if (error < 0) | ||
1070 | goto error; | ||
1071 | |||
1072 | dprintk("Referral FSID: %llx:%llx\n", | ||
1073 | server->fsid.major, server->fsid.minor); | ||
1074 | |||
1075 | spin_lock(&nfs_client_lock); | ||
1076 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1077 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1078 | spin_unlock(&nfs_client_lock); | ||
1079 | |||
1080 | server->mount_time = jiffies; | ||
1081 | |||
1082 | dprintk("<-- nfs_create_referral_server() = %p\n", server); | ||
1083 | return server; | ||
1084 | |||
1085 | error: | ||
1086 | nfs_free_server(server); | ||
1087 | dprintk("<-- nfs4_create_referral_server() = error %d\n", error); | ||
1088 | return ERR_PTR(error); | ||
1089 | } | ||
1090 | |||
1091 | #endif /* CONFIG_NFS_V4 */ | ||
1092 | |||
1093 | /* | ||
1094 | * Clone an NFS2, NFS3 or NFS4 server record | ||
1095 | */ | ||
1096 | struct nfs_server *nfs_clone_server(struct nfs_server *source, | ||
1097 | struct nfs_fh *fh, | ||
1098 | struct nfs_fattr *fattr) | ||
1099 | { | ||
1100 | struct nfs_server *server; | ||
1101 | struct nfs_fattr fattr_fsinfo; | ||
1102 | int error; | ||
1103 | |||
1104 | dprintk("--> nfs_clone_server(,%llx:%llx,)\n", | ||
1105 | fattr->fsid.major, fattr->fsid.minor); | ||
1106 | |||
1107 | server = nfs_alloc_server(); | ||
1108 | if (!server) | ||
1109 | return ERR_PTR(-ENOMEM); | ||
1110 | |||
1111 | /* Copy data from the source */ | ||
1112 | server->nfs_client = source->nfs_client; | ||
1113 | atomic_inc(&server->nfs_client->cl_count); | ||
1114 | nfs_server_copy_userdata(server, source); | ||
1115 | |||
1116 | server->fsid = fattr->fsid; | ||
1117 | |||
1118 | error = nfs_init_server_rpcclient(server, source->client->cl_auth->au_flavor); | ||
1119 | if (error < 0) | ||
1120 | goto out_free_server; | ||
1121 | if (!IS_ERR(source->client_acl)) | ||
1122 | nfs_init_server_aclclient(server); | ||
1123 | |||
1124 | /* probe the filesystem info for this server filesystem */ | ||
1125 | error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo); | ||
1126 | if (error < 0) | ||
1127 | goto out_free_server; | ||
1128 | |||
1129 | dprintk("Cloned FSID: %llx:%llx\n", | ||
1130 | server->fsid.major, server->fsid.minor); | ||
1131 | |||
1132 | error = nfs_start_lockd(server); | ||
1133 | if (error < 0) | ||
1134 | goto out_free_server; | ||
1135 | |||
1136 | spin_lock(&nfs_client_lock); | ||
1137 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1138 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1139 | spin_unlock(&nfs_client_lock); | ||
1140 | |||
1141 | server->mount_time = jiffies; | ||
1142 | |||
1143 | dprintk("<-- nfs_clone_server() = %p\n", server); | ||
1144 | return server; | ||
1145 | |||
1146 | out_free_server: | ||
1147 | nfs_free_server(server); | ||
1148 | dprintk("<-- nfs_clone_server() = error %d\n", error); | ||
1149 | return ERR_PTR(error); | ||
1150 | } | ||
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 19362712452f..9b496ef4abea 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/pagemap.h> | 31 | #include <linux/pagemap.h> |
32 | #include <linux/smp_lock.h> | 32 | #include <linux/smp_lock.h> |
33 | #include <linux/namei.h> | 33 | #include <linux/namei.h> |
34 | #include <linux/mount.h> | ||
34 | 35 | ||
35 | #include "nfs4_fs.h" | 36 | #include "nfs4_fs.h" |
36 | #include "delegation.h" | 37 | #include "delegation.h" |
@@ -870,14 +871,14 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) | |||
870 | return (nd->intent.open.flags & O_EXCL) != 0; | 871 | return (nd->intent.open.flags & O_EXCL) != 0; |
871 | } | 872 | } |
872 | 873 | ||
873 | static inline int nfs_reval_fsid(struct inode *dir, | 874 | static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir, |
874 | struct nfs_fh *fh, struct nfs_fattr *fattr) | 875 | struct nfs_fh *fh, struct nfs_fattr *fattr) |
875 | { | 876 | { |
876 | struct nfs_server *server = NFS_SERVER(dir); | 877 | struct nfs_server *server = NFS_SERVER(dir); |
877 | 878 | ||
878 | if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) | 879 | if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) |
879 | /* Revalidate fsid on root dir */ | 880 | /* Revalidate fsid on root dir */ |
880 | return __nfs_revalidate_inode(server, dir->i_sb->s_root->d_inode); | 881 | return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode); |
881 | return 0; | 882 | return 0; |
882 | } | 883 | } |
883 | 884 | ||
@@ -913,7 +914,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru | |||
913 | res = ERR_PTR(error); | 914 | res = ERR_PTR(error); |
914 | goto out_unlock; | 915 | goto out_unlock; |
915 | } | 916 | } |
916 | error = nfs_reval_fsid(dir, &fhandle, &fattr); | 917 | error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr); |
917 | if (error < 0) { | 918 | if (error < 0) { |
918 | res = ERR_PTR(error); | 919 | res = ERR_PTR(error); |
919 | goto out_unlock; | 920 | goto out_unlock; |
@@ -922,8 +923,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru | |||
922 | res = (struct dentry *)inode; | 923 | res = (struct dentry *)inode; |
923 | if (IS_ERR(res)) | 924 | if (IS_ERR(res)) |
924 | goto out_unlock; | 925 | goto out_unlock; |
926 | |||
925 | no_entry: | 927 | no_entry: |
926 | res = d_add_unique(dentry, inode); | 928 | res = d_materialise_unique(dentry, inode); |
927 | if (res != NULL) | 929 | if (res != NULL) |
928 | dentry = res; | 930 | dentry = res; |
929 | nfs_renew_times(dentry); | 931 | nfs_renew_times(dentry); |
@@ -1117,11 +1119,13 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) | |||
1117 | dput(dentry); | 1119 | dput(dentry); |
1118 | return NULL; | 1120 | return NULL; |
1119 | } | 1121 | } |
1120 | alias = d_add_unique(dentry, inode); | 1122 | |
1123 | alias = d_materialise_unique(dentry, inode); | ||
1121 | if (alias != NULL) { | 1124 | if (alias != NULL) { |
1122 | dput(dentry); | 1125 | dput(dentry); |
1123 | dentry = alias; | 1126 | dentry = alias; |
1124 | } | 1127 | } |
1128 | |||
1125 | nfs_renew_times(dentry); | 1129 | nfs_renew_times(dentry); |
1126 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 1130 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
1127 | return dentry; | 1131 | return dentry; |
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c new file mode 100644 index 000000000000..977e59088eeb --- /dev/null +++ b/fs/nfs/getroot.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* getroot.c: get the root dentry for an NFS mount | ||
2 | * | ||
3 | * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | |||
16 | #include <linux/time.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/stat.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/sunrpc/clnt.h> | ||
24 | #include <linux/sunrpc/stats.h> | ||
25 | #include <linux/nfs_fs.h> | ||
26 | #include <linux/nfs_mount.h> | ||
27 | #include <linux/nfs4_mount.h> | ||
28 | #include <linux/lockd/bind.h> | ||
29 | #include <linux/smp_lock.h> | ||
30 | #include <linux/seq_file.h> | ||
31 | #include <linux/mount.h> | ||
32 | #include <linux/nfs_idmap.h> | ||
33 | #include <linux/vfs.h> | ||
34 | #include <linux/namei.h> | ||
35 | #include <linux/namespace.h> | ||
36 | |||
37 | #include <asm/system.h> | ||
38 | #include <asm/uaccess.h> | ||
39 | |||
40 | #include "nfs4_fs.h" | ||
41 | #include "delegation.h" | ||
42 | #include "internal.h" | ||
43 | |||
44 | #define NFSDBG_FACILITY NFSDBG_CLIENT | ||
45 | #define NFS_PARANOIA 1 | ||
46 | |||
47 | /* | ||
48 | * get an NFS2/NFS3 root dentry from the root filehandle | ||
49 | */ | ||
50 | struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) | ||
51 | { | ||
52 | struct nfs_server *server = NFS_SB(sb); | ||
53 | struct nfs_fsinfo fsinfo; | ||
54 | struct nfs_fattr fattr; | ||
55 | struct dentry *mntroot; | ||
56 | struct inode *inode; | ||
57 | int error; | ||
58 | |||
59 | /* create a dummy root dentry with dummy inode for this superblock */ | ||
60 | if (!sb->s_root) { | ||
61 | struct nfs_fh dummyfh; | ||
62 | struct dentry *root; | ||
63 | struct inode *iroot; | ||
64 | |||
65 | memset(&dummyfh, 0, sizeof(dummyfh)); | ||
66 | memset(&fattr, 0, sizeof(fattr)); | ||
67 | nfs_fattr_init(&fattr); | ||
68 | fattr.valid = NFS_ATTR_FATTR; | ||
69 | fattr.type = NFDIR; | ||
70 | fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; | ||
71 | fattr.nlink = 2; | ||
72 | |||
73 | iroot = nfs_fhget(sb, &dummyfh, &fattr); | ||
74 | if (IS_ERR(iroot)) | ||
75 | return ERR_PTR(PTR_ERR(iroot)); | ||
76 | |||
77 | root = d_alloc_root(iroot); | ||
78 | if (!root) { | ||
79 | iput(iroot); | ||
80 | return ERR_PTR(-ENOMEM); | ||
81 | } | ||
82 | |||
83 | sb->s_root = root; | ||
84 | } | ||
85 | |||
86 | /* get the actual root for this mount */ | ||
87 | fsinfo.fattr = &fattr; | ||
88 | |||
89 | error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); | ||
90 | if (error < 0) { | ||
91 | dprintk("nfs_get_root: getattr error = %d\n", -error); | ||
92 | return ERR_PTR(error); | ||
93 | } | ||
94 | |||
95 | inode = nfs_fhget(sb, mntfh, fsinfo.fattr); | ||
96 | if (IS_ERR(inode)) { | ||
97 | dprintk("nfs_get_root: get root inode failed\n"); | ||
98 | return ERR_PTR(PTR_ERR(inode)); | ||
99 | } | ||
100 | |||
101 | /* root dentries normally start off anonymous and get spliced in later | ||
102 | * if the dentry tree reaches them; however if the dentry already | ||
103 | * exists, we'll pick it up at this point and use it as the root | ||
104 | */ | ||
105 | mntroot = d_alloc_anon(inode); | ||
106 | if (!mntroot) { | ||
107 | iput(inode); | ||
108 | dprintk("nfs_get_root: get root dentry failed\n"); | ||
109 | return ERR_PTR(-ENOMEM); | ||
110 | } | ||
111 | |||
112 | if (!mntroot->d_op) | ||
113 | mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; | ||
114 | |||
115 | return mntroot; | ||
116 | } | ||
117 | |||
118 | #ifdef CONFIG_NFS_V4 | ||
119 | |||
120 | /* | ||
121 | * Do a simple pathwalk from the root FH of the server to the nominated target | ||
122 | * of the mountpoint | ||
123 | * - give error on symlinks | ||
124 | * - give error on ".." occurring in the path | ||
125 | * - follow traversals | ||
126 | */ | ||
127 | int nfs4_path_walk(struct nfs_server *server, | ||
128 | struct nfs_fh *mntfh, | ||
129 | const char *path) | ||
130 | { | ||
131 | struct nfs_fsinfo fsinfo; | ||
132 | struct nfs_fattr fattr; | ||
133 | struct nfs_fh lastfh; | ||
134 | struct qstr name; | ||
135 | int ret; | ||
136 | //int referral_count = 0; | ||
137 | |||
138 | dprintk("--> nfs4_path_walk(,,%s)\n", path); | ||
139 | |||
140 | fsinfo.fattr = &fattr; | ||
141 | nfs_fattr_init(&fattr); | ||
142 | |||
143 | if (*path++ != '/') { | ||
144 | dprintk("nfs4_get_root: Path does not begin with a slash\n"); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | /* Start by getting the root filehandle from the server */ | ||
149 | ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); | ||
150 | if (ret < 0) { | ||
151 | dprintk("nfs4_get_root: getroot error = %d\n", -ret); | ||
152 | return ret; | ||
153 | } | ||
154 | |||
155 | if (fattr.type != NFDIR) { | ||
156 | printk(KERN_ERR "nfs4_get_root:" | ||
157 | " getroot encountered non-directory\n"); | ||
158 | return -ENOTDIR; | ||
159 | } | ||
160 | |||
161 | if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { | ||
162 | printk(KERN_ERR "nfs4_get_root:" | ||
163 | " getroot obtained referral\n"); | ||
164 | return -EREMOTE; | ||
165 | } | ||
166 | |||
167 | next_component: | ||
168 | dprintk("Next: %s\n", path); | ||
169 | |||
170 | /* extract the next bit of the path */ | ||
171 | if (!*path) | ||
172 | goto path_walk_complete; | ||
173 | |||
174 | name.name = path; | ||
175 | while (*path && *path != '/') | ||
176 | path++; | ||
177 | name.len = path - (const char *) name.name; | ||
178 | |||
179 | eat_dot_dir: | ||
180 | while (*path == '/') | ||
181 | path++; | ||
182 | |||
183 | if (path[0] == '.' && (path[1] == '/' || !path[1])) { | ||
184 | path += 2; | ||
185 | goto eat_dot_dir; | ||
186 | } | ||
187 | |||
188 | if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) | ||
189 | ) { | ||
190 | printk(KERN_ERR "nfs4_get_root:" | ||
191 | " Mount path contains reference to \"..\"\n"); | ||
192 | return -EINVAL; | ||
193 | } | ||
194 | |||
195 | /* lookup the next FH in the sequence */ | ||
196 | memcpy(&lastfh, mntfh, sizeof(lastfh)); | ||
197 | |||
198 | dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); | ||
199 | |||
200 | ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, | ||
201 | mntfh, &fattr); | ||
202 | if (ret < 0) { | ||
203 | dprintk("nfs4_get_root: getroot error = %d\n", -ret); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | if (fattr.type != NFDIR) { | ||
208 | printk(KERN_ERR "nfs4_get_root:" | ||
209 | " lookupfh encountered non-directory\n"); | ||
210 | return -ENOTDIR; | ||
211 | } | ||
212 | |||
213 | if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { | ||
214 | printk(KERN_ERR "nfs4_get_root:" | ||
215 | " lookupfh obtained referral\n"); | ||
216 | return -EREMOTE; | ||
217 | } | ||
218 | |||
219 | goto next_component; | ||
220 | |||
221 | path_walk_complete: | ||
222 | memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); | ||
223 | dprintk("<-- nfs4_path_walk() = 0\n"); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * get an NFS4 root dentry from the root filehandle | ||
229 | */ | ||
230 | struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) | ||
231 | { | ||
232 | struct nfs_server *server = NFS_SB(sb); | ||
233 | struct nfs_fattr fattr; | ||
234 | struct dentry *mntroot; | ||
235 | struct inode *inode; | ||
236 | int error; | ||
237 | |||
238 | dprintk("--> nfs4_get_root()\n"); | ||
239 | |||
240 | /* create a dummy root dentry with dummy inode for this superblock */ | ||
241 | if (!sb->s_root) { | ||
242 | struct nfs_fh dummyfh; | ||
243 | struct dentry *root; | ||
244 | struct inode *iroot; | ||
245 | |||
246 | memset(&dummyfh, 0, sizeof(dummyfh)); | ||
247 | memset(&fattr, 0, sizeof(fattr)); | ||
248 | nfs_fattr_init(&fattr); | ||
249 | fattr.valid = NFS_ATTR_FATTR; | ||
250 | fattr.type = NFDIR; | ||
251 | fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; | ||
252 | fattr.nlink = 2; | ||
253 | |||
254 | iroot = nfs_fhget(sb, &dummyfh, &fattr); | ||
255 | if (IS_ERR(iroot)) | ||
256 | return ERR_PTR(PTR_ERR(iroot)); | ||
257 | |||
258 | root = d_alloc_root(iroot); | ||
259 | if (!root) { | ||
260 | iput(iroot); | ||
261 | return ERR_PTR(-ENOMEM); | ||
262 | } | ||
263 | |||
264 | sb->s_root = root; | ||
265 | } | ||
266 | |||
267 | /* get the info about the server and filesystem */ | ||
268 | error = nfs4_server_capabilities(server, mntfh); | ||
269 | if (error < 0) { | ||
270 | dprintk("nfs_get_root: getcaps error = %d\n", | ||
271 | -error); | ||
272 | return ERR_PTR(error); | ||
273 | } | ||
274 | |||
275 | /* get the actual root for this mount */ | ||
276 | error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); | ||
277 | if (error < 0) { | ||
278 | dprintk("nfs_get_root: getattr error = %d\n", -error); | ||
279 | return ERR_PTR(error); | ||
280 | } | ||
281 | |||
282 | inode = nfs_fhget(sb, mntfh, &fattr); | ||
283 | if (IS_ERR(inode)) { | ||
284 | dprintk("nfs_get_root: get root inode failed\n"); | ||
285 | return ERR_PTR(PTR_ERR(inode)); | ||
286 | } | ||
287 | |||
288 | /* root dentries normally start off anonymous and get spliced in later | ||
289 | * if the dentry tree reaches them; however if the dentry already | ||
290 | * exists, we'll pick it up at this point and use it as the root | ||
291 | */ | ||
292 | mntroot = d_alloc_anon(inode); | ||
293 | if (!mntroot) { | ||
294 | iput(inode); | ||
295 | dprintk("nfs_get_root: get root dentry failed\n"); | ||
296 | return ERR_PTR(-ENOMEM); | ||
297 | } | ||
298 | |||
299 | if (!mntroot->d_op) | ||
300 | mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; | ||
301 | |||
302 | dprintk("<-- nfs4_get_root()\n"); | ||
303 | return mntroot; | ||
304 | } | ||
305 | |||
306 | #endif /* CONFIG_NFS_V4 */ | ||
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 231c20ffc0ff..f96dfac7dc9a 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c | |||
@@ -114,8 +114,7 @@ nfs_idmap_new(struct nfs_client *clp) | |||
114 | struct idmap *idmap; | 114 | struct idmap *idmap; |
115 | int error; | 115 | int error; |
116 | 116 | ||
117 | if (clp->cl_idmap != NULL) | 117 | BUG_ON(clp->cl_idmap != NULL); |
118 | return 0; | ||
119 | 118 | ||
120 | if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL) | 119 | if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL) |
121 | return -ENOMEM; | 120 | return -ENOMEM; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 771c3b833757..a547c58a83e6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -1020,7 +1020,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | |||
1020 | out_fileid: | 1020 | out_fileid: |
1021 | printk(KERN_ERR "NFS: server %s error: fileid changed\n" | 1021 | printk(KERN_ERR "NFS: server %s error: fileid changed\n" |
1022 | "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", | 1022 | "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", |
1023 | NFS_SERVER(inode)->hostname, inode->i_sb->s_id, | 1023 | NFS_SERVER(inode)->nfs_client->cl_hostname, inode->i_sb->s_id, |
1024 | (long long)nfsi->fileid, (long long)fattr->fileid); | 1024 | (long long)nfsi->fileid, (long long)fattr->fileid); |
1025 | goto out_err; | 1025 | goto out_err; |
1026 | } | 1026 | } |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 2f3aa52fbefc..e73ba4f1052a 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -4,6 +4,18 @@ | |||
4 | 4 | ||
5 | #include <linux/mount.h> | 5 | #include <linux/mount.h> |
6 | 6 | ||
7 | struct nfs_string; | ||
8 | struct nfs_mount_data; | ||
9 | struct nfs4_mount_data; | ||
10 | |||
11 | /* Maximum number of readahead requests | ||
12 | * FIXME: this should really be a sysctl so that users may tune it to suit | ||
13 | * their needs. People that do NFS over a slow network, might for | ||
14 | * instance want to reduce it to something closer to 1 for improved | ||
15 | * interactive response. | ||
16 | */ | ||
17 | #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) | ||
18 | |||
7 | struct nfs_clone_mount { | 19 | struct nfs_clone_mount { |
8 | const struct super_block *sb; | 20 | const struct super_block *sb; |
9 | const struct dentry *dentry; | 21 | const struct dentry *dentry; |
@@ -16,12 +28,25 @@ struct nfs_clone_mount { | |||
16 | }; | 28 | }; |
17 | 29 | ||
18 | /* client.c */ | 30 | /* client.c */ |
31 | extern struct rpc_program nfs_program; | ||
32 | |||
19 | extern void nfs_put_client(struct nfs_client *); | 33 | extern void nfs_put_client(struct nfs_client *); |
20 | extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int); | 34 | extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int); |
21 | extern struct nfs_client *nfs_get_client(const char *, const struct sockaddr_in *, int); | 35 | extern struct nfs_server *nfs_create_server(const struct nfs_mount_data *, |
22 | extern void nfs_mark_client_ready(struct nfs_client *, int); | 36 | struct nfs_fh *); |
23 | extern int nfs_create_rpc_client(struct nfs_client *, int, unsigned int, | 37 | extern struct nfs_server *nfs4_create_server(const struct nfs4_mount_data *, |
24 | unsigned int, rpc_authflavor_t); | 38 | const char *, |
39 | const struct sockaddr_in *, | ||
40 | const char *, | ||
41 | const char *, | ||
42 | rpc_authflavor_t, | ||
43 | struct nfs_fh *); | ||
44 | extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, | ||
45 | struct nfs_fh *); | ||
46 | extern void nfs_free_server(struct nfs_server *server); | ||
47 | extern struct nfs_server *nfs_clone_server(struct nfs_server *, | ||
48 | struct nfs_fh *, | ||
49 | struct nfs_fattr *); | ||
25 | 50 | ||
26 | /* nfs4namespace.c */ | 51 | /* nfs4namespace.c */ |
27 | #ifdef CONFIG_NFS_V4 | 52 | #ifdef CONFIG_NFS_V4 |
@@ -89,10 +114,10 @@ extern void nfs4_clear_inode(struct inode *); | |||
89 | #endif | 114 | #endif |
90 | 115 | ||
91 | /* super.c */ | 116 | /* super.c */ |
92 | extern struct file_system_type nfs_referral_nfs4_fs_type; | 117 | extern struct file_system_type nfs_xdev_fs_type; |
93 | extern struct file_system_type clone_nfs_fs_type; | ||
94 | #ifdef CONFIG_NFS_V4 | 118 | #ifdef CONFIG_NFS_V4 |
95 | extern struct file_system_type clone_nfs4_fs_type; | 119 | extern struct file_system_type nfs4_xdev_fs_type; |
120 | extern struct file_system_type nfs4_referral_fs_type; | ||
96 | #endif | 121 | #endif |
97 | 122 | ||
98 | extern struct rpc_stat nfs_rpcstat; | 123 | extern struct rpc_stat nfs_rpcstat; |
@@ -101,28 +126,30 @@ extern int __init register_nfs_fs(void); | |||
101 | extern void __exit unregister_nfs_fs(void); | 126 | extern void __exit unregister_nfs_fs(void); |
102 | 127 | ||
103 | /* namespace.c */ | 128 | /* namespace.c */ |
104 | extern char *nfs_path(const char *base, const struct dentry *dentry, | 129 | extern char *nfs_path(const char *base, |
130 | const struct dentry *droot, | ||
131 | const struct dentry *dentry, | ||
105 | char *buffer, ssize_t buflen); | 132 | char *buffer, ssize_t buflen); |
106 | 133 | ||
107 | /* | 134 | /* getroot.c */ |
108 | * Determine the mount path as a string | 135 | extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); |
109 | */ | ||
110 | #ifdef CONFIG_NFS_V4 | 136 | #ifdef CONFIG_NFS_V4 |
111 | static inline char * | 137 | extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *); |
112 | nfs4_path(const struct dentry *dentry, char *buffer, ssize_t buflen) | 138 | |
113 | { | 139 | extern int nfs4_path_walk(struct nfs_server *server, |
114 | return nfs_path(NFS_SB(dentry->d_sb)->mnt_path, dentry, buffer, buflen); | 140 | struct nfs_fh *mntfh, |
115 | } | 141 | const char *path); |
116 | #endif | 142 | #endif |
117 | 143 | ||
118 | /* | 144 | /* |
119 | * Determine the device name as a string | 145 | * Determine the device name as a string |
120 | */ | 146 | */ |
121 | static inline char *nfs_devname(const struct vfsmount *mnt_parent, | 147 | static inline char *nfs_devname(const struct vfsmount *mnt_parent, |
122 | const struct dentry *dentry, | 148 | const struct dentry *dentry, |
123 | char *buffer, ssize_t buflen) | 149 | char *buffer, ssize_t buflen) |
124 | { | 150 | { |
125 | return nfs_path(mnt_parent->mnt_devname, dentry, buffer, buflen); | 151 | return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root, |
152 | dentry, buffer, buflen); | ||
126 | } | 153 | } |
127 | 154 | ||
128 | /* | 155 | /* |
@@ -178,20 +205,3 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) | |||
178 | if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) | 205 | if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) |
179 | sb->s_maxbytes = MAX_LFS_FILESIZE; | 206 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
180 | } | 207 | } |
181 | |||
182 | /* | ||
183 | * Check if the string represents a "valid" IPv4 address | ||
184 | */ | ||
185 | static inline int valid_ipaddr4(const char *buf) | ||
186 | { | ||
187 | int rc, count, in[4]; | ||
188 | |||
189 | rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]); | ||
190 | if (rc != 4) | ||
191 | return -EINVAL; | ||
192 | for (count = 0; count < 4; count++) { | ||
193 | if (in[count] > 255) | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index d8b8d56266cb..77b00684894d 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * linux/fs/nfs/namespace.c | 2 | * linux/fs/nfs/namespace.c |
3 | * | 3 | * |
4 | * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> | 4 | * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> |
5 | * - Modified by David Howells <dhowells@redhat.com> | ||
5 | * | 6 | * |
6 | * NFS namespace | 7 | * NFS namespace |
7 | */ | 8 | */ |
@@ -28,6 +29,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ; | |||
28 | /* | 29 | /* |
29 | * nfs_path - reconstruct the path given an arbitrary dentry | 30 | * nfs_path - reconstruct the path given an arbitrary dentry |
30 | * @base - arbitrary string to prepend to the path | 31 | * @base - arbitrary string to prepend to the path |
32 | * @droot - pointer to root dentry for mountpoint | ||
31 | * @dentry - pointer to dentry | 33 | * @dentry - pointer to dentry |
32 | * @buffer - result buffer | 34 | * @buffer - result buffer |
33 | * @buflen - length of buffer | 35 | * @buflen - length of buffer |
@@ -38,7 +40,9 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ; | |||
38 | * This is mainly for use in figuring out the path on the | 40 | * This is mainly for use in figuring out the path on the |
39 | * server side when automounting on top of an existing partition. | 41 | * server side when automounting on top of an existing partition. |
40 | */ | 42 | */ |
41 | char *nfs_path(const char *base, const struct dentry *dentry, | 43 | char *nfs_path(const char *base, |
44 | const struct dentry *droot, | ||
45 | const struct dentry *dentry, | ||
42 | char *buffer, ssize_t buflen) | 46 | char *buffer, ssize_t buflen) |
43 | { | 47 | { |
44 | char *end = buffer+buflen; | 48 | char *end = buffer+buflen; |
@@ -47,7 +51,7 @@ char *nfs_path(const char *base, const struct dentry *dentry, | |||
47 | *--end = '\0'; | 51 | *--end = '\0'; |
48 | buflen--; | 52 | buflen--; |
49 | spin_lock(&dcache_lock); | 53 | spin_lock(&dcache_lock); |
50 | while (!IS_ROOT(dentry)) { | 54 | while (!IS_ROOT(dentry) && dentry != droot) { |
51 | namelen = dentry->d_name.len; | 55 | namelen = dentry->d_name.len; |
52 | buflen -= namelen + 1; | 56 | buflen -= namelen + 1; |
53 | if (buflen < 0) | 57 | if (buflen < 0) |
@@ -96,12 +100,13 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
96 | struct nfs_fattr fattr; | 100 | struct nfs_fattr fattr; |
97 | int err; | 101 | int err; |
98 | 102 | ||
103 | dprintk("--> nfs_follow_mountpoint()\n"); | ||
104 | |||
99 | BUG_ON(IS_ROOT(dentry)); | 105 | BUG_ON(IS_ROOT(dentry)); |
100 | dprintk("%s: enter\n", __FUNCTION__); | 106 | dprintk("%s: enter\n", __FUNCTION__); |
101 | dput(nd->dentry); | 107 | dput(nd->dentry); |
102 | nd->dentry = dget(dentry); | 108 | nd->dentry = dget(dentry); |
103 | if (d_mountpoint(nd->dentry)) | 109 | |
104 | goto out_follow; | ||
105 | /* Look it up again */ | 110 | /* Look it up again */ |
106 | parent = dget_parent(nd->dentry); | 111 | parent = dget_parent(nd->dentry); |
107 | err = server->nfs_client->rpc_ops->lookup(parent->d_inode, | 112 | err = server->nfs_client->rpc_ops->lookup(parent->d_inode, |
@@ -134,6 +139,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
134 | schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); | 139 | schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); |
135 | out: | 140 | out: |
136 | dprintk("%s: done, returned %d\n", __FUNCTION__, err); | 141 | dprintk("%s: done, returned %d\n", __FUNCTION__, err); |
142 | |||
143 | dprintk("<-- nfs_follow_mountpoint() = %d\n", err); | ||
137 | return ERR_PTR(err); | 144 | return ERR_PTR(err); |
138 | out_err: | 145 | out_err: |
139 | path_release(nd); | 146 | path_release(nd); |
@@ -183,14 +190,14 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, | |||
183 | switch (server->nfs_client->cl_nfsversion) { | 190 | switch (server->nfs_client->cl_nfsversion) { |
184 | case 2: | 191 | case 2: |
185 | case 3: | 192 | case 3: |
186 | mnt = vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); | 193 | mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); |
187 | break; | 194 | break; |
188 | case 4: | 195 | case 4: |
189 | mnt = vfs_kern_mount(&clone_nfs4_fs_type, 0, devname, mountdata); | 196 | mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata); |
190 | } | 197 | } |
191 | return mnt; | 198 | return mnt; |
192 | #else | 199 | #else |
193 | return vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); | 200 | return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); |
194 | #endif | 201 | #endif |
195 | } | 202 | } |
196 | 203 | ||
@@ -216,6 +223,8 @@ struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, | |||
216 | char *page = (char *) __get_free_page(GFP_USER); | 223 | char *page = (char *) __get_free_page(GFP_USER); |
217 | char *devname; | 224 | char *devname; |
218 | 225 | ||
226 | dprintk("--> nfs_do_submount()\n"); | ||
227 | |||
219 | dprintk("%s: submounting on %s/%s\n", __FUNCTION__, | 228 | dprintk("%s: submounting on %s/%s\n", __FUNCTION__, |
220 | dentry->d_parent->d_name.name, | 229 | dentry->d_parent->d_name.name, |
221 | dentry->d_name.name); | 230 | dentry->d_name.name); |
@@ -230,5 +239,7 @@ free_page: | |||
230 | free_page((unsigned long)page); | 239 | free_page((unsigned long)page); |
231 | out: | 240 | out: |
232 | dprintk("%s: done\n", __FUNCTION__); | 241 | dprintk("%s: done\n", __FUNCTION__); |
242 | |||
243 | dprintk("<-- nfs_do_submount() = %p\n", mnt); | ||
233 | return mnt; | 244 | return mnt; |
234 | } | 245 | } |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 0622af0122be..9e8258ece6fd 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -81,7 +81,7 @@ do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle, | |||
81 | } | 81 | } |
82 | 82 | ||
83 | /* | 83 | /* |
84 | * Bare-bones access to getattr: this is for nfs_read_super. | 84 | * Bare-bones access to getattr: this is for nfs_get_root/nfs_get_sb |
85 | */ | 85 | */ |
86 | static int | 86 | static int |
87 | nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, | 87 | nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, |
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index e7879245361e..61095fe4b5ca 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -188,8 +188,6 @@ extern void nfs4_kill_renewd(struct nfs_client *); | |||
188 | extern void nfs4_renew_state(void *); | 188 | extern void nfs4_renew_state(void *); |
189 | 189 | ||
190 | /* nfs4state.c */ | 190 | /* nfs4state.c */ |
191 | extern void init_nfsv4_state(struct nfs_server *); | ||
192 | extern void destroy_nfsv4_state(struct nfs_server *); | ||
193 | struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); | 191 | struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); |
194 | extern u32 nfs4_alloc_lockowner_id(struct nfs_client *); | 192 | extern u32 nfs4_alloc_lockowner_id(struct nfs_client *); |
195 | 193 | ||
@@ -224,10 +222,6 @@ extern struct svc_version nfs4_callback_version1; | |||
224 | 222 | ||
225 | #else | 223 | #else |
226 | 224 | ||
227 | #define init_nfsv4_state(server) do { } while (0) | ||
228 | #define destroy_nfsv4_state(server) do { } while (0) | ||
229 | #define nfs4_put_state_owner(inode, owner) do { } while (0) | ||
230 | #define nfs4_put_open_state(state) do { } while (0) | ||
231 | #define nfs4_close_state(a, b) do { } while (0) | 225 | #define nfs4_close_state(a, b) do { } while (0) |
232 | 226 | ||
233 | #endif /* CONFIG_NFS_V4 */ | 227 | #endif /* CONFIG_NFS_V4 */ |
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index faed9bcba50f..24e47f3bbd17 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * linux/fs/nfs/nfs4namespace.c | 2 | * linux/fs/nfs/nfs4namespace.c |
3 | * | 3 | * |
4 | * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> | 4 | * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> |
5 | * - Modified by David Howells <dhowells@redhat.com> | ||
5 | * | 6 | * |
6 | * NFSv4 namespace | 7 | * NFSv4 namespace |
7 | */ | 8 | */ |
@@ -47,6 +48,68 @@ Elong: | |||
47 | return ERR_PTR(-ENAMETOOLONG); | 48 | return ERR_PTR(-ENAMETOOLONG); |
48 | } | 49 | } |
49 | 50 | ||
51 | /* | ||
52 | * Determine the mount path as a string | ||
53 | */ | ||
54 | static char *nfs4_path(const struct vfsmount *mnt_parent, | ||
55 | const struct dentry *dentry, | ||
56 | char *buffer, ssize_t buflen) | ||
57 | { | ||
58 | const char *srvpath; | ||
59 | |||
60 | srvpath = strchr(mnt_parent->mnt_devname, ':'); | ||
61 | if (srvpath) | ||
62 | srvpath++; | ||
63 | else | ||
64 | srvpath = mnt_parent->mnt_devname; | ||
65 | |||
66 | return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we | ||
71 | * believe to be the server path to this dentry | ||
72 | */ | ||
73 | static int nfs4_validate_fspath(const struct vfsmount *mnt_parent, | ||
74 | const struct dentry *dentry, | ||
75 | const struct nfs4_fs_locations *locations, | ||
76 | char *page, char *page2) | ||
77 | { | ||
78 | const char *path, *fs_path; | ||
79 | |||
80 | path = nfs4_path(mnt_parent, dentry, page, PAGE_SIZE); | ||
81 | if (IS_ERR(path)) | ||
82 | return PTR_ERR(path); | ||
83 | |||
84 | fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); | ||
85 | if (IS_ERR(fs_path)) | ||
86 | return PTR_ERR(fs_path); | ||
87 | |||
88 | if (strncmp(path, fs_path, strlen(fs_path)) != 0) { | ||
89 | dprintk("%s: path %s does not begin with fsroot %s\n", | ||
90 | __FUNCTION__, path, fs_path); | ||
91 | return -ENOENT; | ||
92 | } | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Check if the string represents a "valid" IPv4 address | ||
99 | */ | ||
100 | static inline int valid_ipaddr4(const char *buf) | ||
101 | { | ||
102 | int rc, count, in[4]; | ||
103 | |||
104 | rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]); | ||
105 | if (rc != 4) | ||
106 | return -EINVAL; | ||
107 | for (count = 0; count < 4; count++) { | ||
108 | if (in[count] > 255) | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
50 | 113 | ||
51 | /** | 114 | /** |
52 | * nfs_follow_referral - set up mountpoint when hitting a referral on moved error | 115 | * nfs_follow_referral - set up mountpoint when hitting a referral on moved error |
@@ -68,10 +131,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, | |||
68 | .dentry = dentry, | 131 | .dentry = dentry, |
69 | .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, | 132 | .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, |
70 | }; | 133 | }; |
71 | char *page, *page2; | 134 | char *page = NULL, *page2 = NULL; |
72 | char *path, *fs_path; | ||
73 | char *devname; | 135 | char *devname; |
74 | int loc, s; | 136 | int loc, s, error; |
75 | 137 | ||
76 | if (locations == NULL || locations->nlocations <= 0) | 138 | if (locations == NULL || locations->nlocations <= 0) |
77 | goto out; | 139 | goto out; |
@@ -79,31 +141,25 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, | |||
79 | dprintk("%s: referral at %s/%s\n", __FUNCTION__, | 141 | dprintk("%s: referral at %s/%s\n", __FUNCTION__, |
80 | dentry->d_parent->d_name.name, dentry->d_name.name); | 142 | dentry->d_parent->d_name.name, dentry->d_name.name); |
81 | 143 | ||
82 | /* Ensure fs path is a prefix of current dentry path */ | ||
83 | page = (char *) __get_free_page(GFP_USER); | 144 | page = (char *) __get_free_page(GFP_USER); |
84 | if (page == NULL) | 145 | if (!page) |
85 | goto out; | 146 | goto out; |
147 | |||
86 | page2 = (char *) __get_free_page(GFP_USER); | 148 | page2 = (char *) __get_free_page(GFP_USER); |
87 | if (page2 == NULL) | 149 | if (!page2) |
88 | goto out; | 150 | goto out; |
89 | 151 | ||
90 | path = nfs4_path(dentry, page, PAGE_SIZE); | 152 | /* Ensure fs path is a prefix of current dentry path */ |
91 | if (IS_ERR(path)) | 153 | error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2); |
92 | goto out_free; | 154 | if (error < 0) { |
93 | 155 | mnt = ERR_PTR(error); | |
94 | fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); | 156 | goto out; |
95 | if (IS_ERR(fs_path)) | ||
96 | goto out_free; | ||
97 | |||
98 | if (strncmp(path, fs_path, strlen(fs_path)) != 0) { | ||
99 | dprintk("%s: path %s does not begin with fsroot %s\n", __FUNCTION__, path, fs_path); | ||
100 | goto out_free; | ||
101 | } | 157 | } |
102 | 158 | ||
103 | devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); | 159 | devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); |
104 | if (IS_ERR(devname)) { | 160 | if (IS_ERR(devname)) { |
105 | mnt = (struct vfsmount *)devname; | 161 | mnt = (struct vfsmount *)devname; |
106 | goto out_free; | 162 | goto out; |
107 | } | 163 | } |
108 | 164 | ||
109 | loc = 0; | 165 | loc = 0; |
@@ -140,7 +196,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, | |||
140 | addr.sin_port = htons(NFS_PORT); | 196 | addr.sin_port = htons(NFS_PORT); |
141 | mountdata.addr = &addr; | 197 | mountdata.addr = &addr; |
142 | 198 | ||
143 | mnt = vfs_kern_mount(&nfs_referral_nfs4_fs_type, 0, devname, &mountdata); | 199 | mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, devname, &mountdata); |
144 | if (!IS_ERR(mnt)) { | 200 | if (!IS_ERR(mnt)) { |
145 | break; | 201 | break; |
146 | } | 202 | } |
@@ -149,10 +205,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, | |||
149 | loc++; | 205 | loc++; |
150 | } | 206 | } |
151 | 207 | ||
152 | out_free: | ||
153 | free_page((unsigned long)page); | ||
154 | free_page((unsigned long)page2); | ||
155 | out: | 208 | out: |
209 | free_page((unsigned long) page); | ||
210 | free_page((unsigned long) page2); | ||
156 | dprintk("%s: done\n", __FUNCTION__); | 211 | dprintk("%s: done\n", __FUNCTION__); |
157 | return mnt; | 212 | return mnt; |
158 | } | 213 | } |
@@ -165,7 +220,7 @@ out: | |||
165 | */ | 220 | */ |
166 | struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) | 221 | struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) |
167 | { | 222 | { |
168 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | 223 | struct vfsmount *mnt = ERR_PTR(-ENOMEM); |
169 | struct dentry *parent; | 224 | struct dentry *parent; |
170 | struct nfs4_fs_locations *fs_locations = NULL; | 225 | struct nfs4_fs_locations *fs_locations = NULL; |
171 | struct page *page; | 226 | struct page *page; |
@@ -183,11 +238,16 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr | |||
183 | goto out_free; | 238 | goto out_free; |
184 | 239 | ||
185 | /* Get locations */ | 240 | /* Get locations */ |
241 | mnt = ERR_PTR(-ENOENT); | ||
242 | |||
186 | parent = dget_parent(dentry); | 243 | parent = dget_parent(dentry); |
187 | dprintk("%s: getting locations for %s/%s\n", __FUNCTION__, parent->d_name.name, dentry->d_name.name); | 244 | dprintk("%s: getting locations for %s/%s\n", |
245 | __FUNCTION__, parent->d_name.name, dentry->d_name.name); | ||
246 | |||
188 | err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); | 247 | err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); |
189 | dput(parent); | 248 | dput(parent); |
190 | if (err != 0 || fs_locations->nlocations <= 0 || | 249 | if (err != 0 || |
250 | fs_locations->nlocations <= 0 || | ||
191 | fs_locations->fs_path.ncomponents <= 0) | 251 | fs_locations->fs_path.ncomponents <= 0) |
192 | goto out_free; | 252 | goto out_free; |
193 | 253 | ||
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1573eeb07ce1..a825547e8214 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -1393,70 +1393,19 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, | |||
1393 | return err; | 1393 | return err; |
1394 | } | 1394 | } |
1395 | 1395 | ||
1396 | /* | ||
1397 | * get the file handle for the "/" directory on the server | ||
1398 | */ | ||
1396 | static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, | 1399 | static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, |
1397 | struct nfs_fsinfo *info) | 1400 | struct nfs_fsinfo *info) |
1398 | { | 1401 | { |
1399 | struct nfs_fattr * fattr = info->fattr; | ||
1400 | unsigned char * p; | ||
1401 | struct qstr q; | ||
1402 | struct nfs4_lookup_arg args = { | ||
1403 | .dir_fh = fhandle, | ||
1404 | .name = &q, | ||
1405 | .bitmask = nfs4_fattr_bitmap, | ||
1406 | }; | ||
1407 | struct nfs4_lookup_res res = { | ||
1408 | .server = server, | ||
1409 | .fattr = fattr, | ||
1410 | .fh = fhandle, | ||
1411 | }; | ||
1412 | struct rpc_message msg = { | ||
1413 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP], | ||
1414 | .rpc_argp = &args, | ||
1415 | .rpc_resp = &res, | ||
1416 | }; | ||
1417 | int status; | 1402 | int status; |
1418 | 1403 | ||
1419 | /* | ||
1420 | * Now we do a separate LOOKUP for each component of the mount path. | ||
1421 | * The LOOKUPs are done separately so that we can conveniently | ||
1422 | * catch an ERR_WRONGSEC if it occurs along the way... | ||
1423 | */ | ||
1424 | status = nfs4_lookup_root(server, fhandle, info); | 1404 | status = nfs4_lookup_root(server, fhandle, info); |
1425 | if (status) | ||
1426 | goto out; | ||
1427 | |||
1428 | p = server->mnt_path; | ||
1429 | for (;;) { | ||
1430 | struct nfs4_exception exception = { }; | ||
1431 | |||
1432 | while (*p == '/') | ||
1433 | p++; | ||
1434 | if (!*p) | ||
1435 | break; | ||
1436 | q.name = p; | ||
1437 | while (*p && (*p != '/')) | ||
1438 | p++; | ||
1439 | q.len = p - q.name; | ||
1440 | |||
1441 | do { | ||
1442 | nfs_fattr_init(fattr); | ||
1443 | status = nfs4_handle_exception(server, | ||
1444 | rpc_call_sync(server->client, &msg, 0), | ||
1445 | &exception); | ||
1446 | } while (exception.retry); | ||
1447 | if (status == 0) | ||
1448 | continue; | ||
1449 | if (status == -ENOENT) { | ||
1450 | printk(KERN_NOTICE "NFS: mount path %s does not exist!\n", server->mnt_path); | ||
1451 | printk(KERN_NOTICE "NFS: suggestion: try mounting '/' instead.\n"); | ||
1452 | } | ||
1453 | break; | ||
1454 | } | ||
1455 | if (status == 0) | 1405 | if (status == 0) |
1456 | status = nfs4_server_capabilities(server, fhandle); | 1406 | status = nfs4_server_capabilities(server, fhandle); |
1457 | if (status == 0) | 1407 | if (status == 0) |
1458 | status = nfs4_do_fsinfo(server, fhandle, info); | 1408 | status = nfs4_do_fsinfo(server, fhandle, info); |
1459 | out: | ||
1460 | return nfs4_map_errors(status); | 1409 | return nfs4_map_errors(status); |
1461 | } | 1410 | } |
1462 | 1411 | ||
diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index ff947ecb8b81..f2c893690ac4 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c | |||
@@ -127,26 +127,13 @@ nfs4_schedule_state_renewal(struct nfs_client *clp) | |||
127 | void | 127 | void |
128 | nfs4_renewd_prepare_shutdown(struct nfs_server *server) | 128 | nfs4_renewd_prepare_shutdown(struct nfs_server *server) |
129 | { | 129 | { |
130 | struct nfs_client *clp = server->nfs_client; | ||
131 | |||
132 | if (!clp) | ||
133 | return; | ||
134 | flush_scheduled_work(); | 130 | flush_scheduled_work(); |
135 | down_write(&clp->cl_sem); | ||
136 | if (!list_empty(&server->nfs4_siblings)) | ||
137 | list_del_init(&server->nfs4_siblings); | ||
138 | up_write(&clp->cl_sem); | ||
139 | } | 131 | } |
140 | 132 | ||
141 | /* Must be called with clp->cl_sem locked for writes */ | ||
142 | void | 133 | void |
143 | nfs4_kill_renewd(struct nfs_client *clp) | 134 | nfs4_kill_renewd(struct nfs_client *clp) |
144 | { | 135 | { |
145 | down_read(&clp->cl_sem); | 136 | down_read(&clp->cl_sem); |
146 | if (!list_empty(&clp->cl_superblocks)) { | ||
147 | up_read(&clp->cl_sem); | ||
148 | return; | ||
149 | } | ||
150 | cancel_delayed_work(&clp->cl_renewd); | 137 | cancel_delayed_work(&clp->cl_renewd); |
151 | up_read(&clp->cl_sem); | 138 | up_read(&clp->cl_sem); |
152 | flush_scheduled_work(); | 139 | flush_scheduled_work(); |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 058811e39555..5fffbdfa971f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -58,24 +58,6 @@ const nfs4_stateid zero_stateid; | |||
58 | 58 | ||
59 | static LIST_HEAD(nfs4_clientid_list); | 59 | static LIST_HEAD(nfs4_clientid_list); |
60 | 60 | ||
61 | void | ||
62 | init_nfsv4_state(struct nfs_server *server) | ||
63 | { | ||
64 | server->nfs_client = NULL; | ||
65 | INIT_LIST_HEAD(&server->nfs4_siblings); | ||
66 | } | ||
67 | |||
68 | void | ||
69 | destroy_nfsv4_state(struct nfs_server *server) | ||
70 | { | ||
71 | kfree(server->mnt_path); | ||
72 | server->mnt_path = NULL; | ||
73 | if (server->nfs_client) { | ||
74 | nfs_put_client(server->nfs_client); | ||
75 | server->nfs_client = NULL; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) | 61 | static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) |
80 | { | 62 | { |
81 | int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, | 63 | int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, |
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index f0aff824a291..dae33c1e8a77 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -171,7 +171,7 @@ static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode, | |||
171 | rdata->args.offset = page_offset(page) + rdata->args.pgbase; | 171 | rdata->args.offset = page_offset(page) + rdata->args.pgbase; |
172 | 172 | ||
173 | dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n", | 173 | dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n", |
174 | NFS_SERVER(inode)->hostname, | 174 | NFS_SERVER(inode)->nfs_client->cl_hostname, |
175 | inode->i_sb->s_id, | 175 | inode->i_sb->s_id, |
176 | (long long)NFS_FILEID(inode), | 176 | (long long)NFS_FILEID(inode), |
177 | (unsigned long long)rdata->args.pgbase, | 177 | (unsigned long long)rdata->args.pgbase, |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5842d510d732..867b5dcd3a40 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -13,6 +13,11 @@ | |||
13 | * | 13 | * |
14 | * Split from inode.c by David Howells <dhowells@redhat.com> | 14 | * Split from inode.c by David Howells <dhowells@redhat.com> |
15 | * | 15 | * |
16 | * - superblocks are indexed on server only - all inodes, dentries, etc. associated with a | ||
17 | * particular server are held in the same superblock | ||
18 | * - NFS superblocks can have several effective roots to the dentry tree | ||
19 | * - directory type roots are spliced into the tree when a path from one root reaches the root | ||
20 | * of another (see nfs_lookup()) | ||
16 | */ | 21 | */ |
17 | 22 | ||
18 | #include <linux/config.h> | 23 | #include <linux/config.h> |
@@ -52,20 +57,12 @@ | |||
52 | 57 | ||
53 | #define NFSDBG_FACILITY NFSDBG_VFS | 58 | #define NFSDBG_FACILITY NFSDBG_VFS |
54 | 59 | ||
55 | /* Maximum number of readahead requests | ||
56 | * FIXME: this should really be a sysctl so that users may tune it to suit | ||
57 | * their needs. People that do NFS over a slow network, might for | ||
58 | * instance want to reduce it to something closer to 1 for improved | ||
59 | * interactive response. | ||
60 | */ | ||
61 | #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) | ||
62 | |||
63 | static void nfs_umount_begin(struct vfsmount *, int); | 60 | static void nfs_umount_begin(struct vfsmount *, int); |
64 | static int nfs_statfs(struct dentry *, struct kstatfs *); | 61 | static int nfs_statfs(struct dentry *, struct kstatfs *); |
65 | static int nfs_show_options(struct seq_file *, struct vfsmount *); | 62 | static int nfs_show_options(struct seq_file *, struct vfsmount *); |
66 | static int nfs_show_stats(struct seq_file *, struct vfsmount *); | 63 | static int nfs_show_stats(struct seq_file *, struct vfsmount *); |
67 | static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *); | 64 | static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *); |
68 | static int nfs_clone_nfs_sb(struct file_system_type *fs_type, | 65 | static int nfs_xdev_get_sb(struct file_system_type *fs_type, |
69 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | 66 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); |
70 | static void nfs_kill_super(struct super_block *); | 67 | static void nfs_kill_super(struct super_block *); |
71 | 68 | ||
@@ -77,10 +74,10 @@ static struct file_system_type nfs_fs_type = { | |||
77 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | 74 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, |
78 | }; | 75 | }; |
79 | 76 | ||
80 | struct file_system_type clone_nfs_fs_type = { | 77 | struct file_system_type nfs_xdev_fs_type = { |
81 | .owner = THIS_MODULE, | 78 | .owner = THIS_MODULE, |
82 | .name = "nfs", | 79 | .name = "nfs", |
83 | .get_sb = nfs_clone_nfs_sb, | 80 | .get_sb = nfs_xdev_get_sb, |
84 | .kill_sb = nfs_kill_super, | 81 | .kill_sb = nfs_kill_super, |
85 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | 82 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, |
86 | }; | 83 | }; |
@@ -99,10 +96,10 @@ static struct super_operations nfs_sops = { | |||
99 | #ifdef CONFIG_NFS_V4 | 96 | #ifdef CONFIG_NFS_V4 |
100 | static int nfs4_get_sb(struct file_system_type *fs_type, | 97 | static int nfs4_get_sb(struct file_system_type *fs_type, |
101 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | 98 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); |
102 | static int nfs_clone_nfs4_sb(struct file_system_type *fs_type, | 99 | static int nfs4_xdev_get_sb(struct file_system_type *fs_type, |
103 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | 100 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); |
104 | static int nfs_referral_nfs4_sb(struct file_system_type *fs_type, | 101 | static int nfs4_referral_get_sb(struct file_system_type *fs_type, |
105 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | 102 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); |
106 | static void nfs4_kill_super(struct super_block *sb); | 103 | static void nfs4_kill_super(struct super_block *sb); |
107 | 104 | ||
108 | static struct file_system_type nfs4_fs_type = { | 105 | static struct file_system_type nfs4_fs_type = { |
@@ -113,18 +110,18 @@ static struct file_system_type nfs4_fs_type = { | |||
113 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | 110 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, |
114 | }; | 111 | }; |
115 | 112 | ||
116 | struct file_system_type clone_nfs4_fs_type = { | 113 | struct file_system_type nfs4_xdev_fs_type = { |
117 | .owner = THIS_MODULE, | 114 | .owner = THIS_MODULE, |
118 | .name = "nfs4", | 115 | .name = "nfs4", |
119 | .get_sb = nfs_clone_nfs4_sb, | 116 | .get_sb = nfs4_xdev_get_sb, |
120 | .kill_sb = nfs4_kill_super, | 117 | .kill_sb = nfs4_kill_super, |
121 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | 118 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, |
122 | }; | 119 | }; |
123 | 120 | ||
124 | struct file_system_type nfs_referral_nfs4_fs_type = { | 121 | struct file_system_type nfs4_referral_fs_type = { |
125 | .owner = THIS_MODULE, | 122 | .owner = THIS_MODULE, |
126 | .name = "nfs4", | 123 | .name = "nfs4", |
127 | .get_sb = nfs_referral_nfs4_sb, | 124 | .get_sb = nfs4_referral_get_sb, |
128 | .kill_sb = nfs4_kill_super, | 125 | .kill_sb = nfs4_kill_super, |
129 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | 126 | .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, |
130 | }; | 127 | }; |
@@ -345,7 +342,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
345 | nfs_show_mount_options(m, nfss, 0); | 342 | nfs_show_mount_options(m, nfss, 0); |
346 | 343 | ||
347 | seq_puts(m, ",addr="); | 344 | seq_puts(m, ",addr="); |
348 | seq_escape(m, nfss->hostname, " \t\n\\"); | 345 | seq_escape(m, nfss->nfs_client->cl_hostname, " \t\n\\"); |
349 | 346 | ||
350 | return 0; | 347 | return 0; |
351 | } | 348 | } |
@@ -429,714 +426,351 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) | |||
429 | 426 | ||
430 | /* | 427 | /* |
431 | * Begin unmount by attempting to remove all automounted mountpoints we added | 428 | * Begin unmount by attempting to remove all automounted mountpoints we added |
432 | * in response to traversals | 429 | * in response to xdev traversals and referrals |
433 | */ | 430 | */ |
434 | static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) | 431 | static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) |
435 | { | 432 | { |
436 | struct nfs_server *server; | ||
437 | struct rpc_clnt *rpc; | ||
438 | |||
439 | shrink_submounts(vfsmnt, &nfs_automount_list); | 433 | shrink_submounts(vfsmnt, &nfs_automount_list); |
440 | if (!(flags & MNT_FORCE)) | ||
441 | return; | ||
442 | /* -EIO all pending I/O */ | ||
443 | server = NFS_SB(vfsmnt->mnt_sb); | ||
444 | rpc = server->client; | ||
445 | if (!IS_ERR(rpc)) | ||
446 | rpc_killall_tasks(rpc); | ||
447 | rpc = server->client_acl; | ||
448 | if (!IS_ERR(rpc)) | ||
449 | rpc_killall_tasks(rpc); | ||
450 | } | 434 | } |
451 | 435 | ||
452 | /* | 436 | /* |
453 | * Obtain the root inode of the file system. | 437 | * Validate the NFS2/NFS3 mount data |
438 | * - fills in the mount root filehandle | ||
454 | */ | 439 | */ |
455 | static struct inode * | 440 | static int nfs_validate_mount_data(struct nfs_mount_data *data, |
456 | nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo) | 441 | struct nfs_fh *mntfh) |
457 | { | 442 | { |
458 | struct nfs_server *server = NFS_SB(sb); | 443 | if (data == NULL) { |
459 | int error; | 444 | dprintk("%s: missing data argument\n", __FUNCTION__); |
460 | 445 | return -EINVAL; | |
461 | error = server->nfs_client->rpc_ops->getroot(server, rootfh, fsinfo); | ||
462 | if (error < 0) { | ||
463 | dprintk("nfs_get_root: getattr error = %d\n", -error); | ||
464 | return ERR_PTR(error); | ||
465 | } | 446 | } |
466 | 447 | ||
467 | server->fsid = fsinfo->fattr->fsid; | 448 | if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { |
468 | return nfs_fhget(sb, rootfh, fsinfo->fattr); | 449 | dprintk("%s: bad mount version\n", __FUNCTION__); |
469 | } | 450 | return -EINVAL; |
470 | 451 | } | |
471 | /* | ||
472 | * Do NFS version-independent mount processing, and sanity checking | ||
473 | */ | ||
474 | static int | ||
475 | nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) | ||
476 | { | ||
477 | struct nfs_server *server; | ||
478 | struct inode *root_inode; | ||
479 | struct nfs_fattr fattr; | ||
480 | struct nfs_fsinfo fsinfo = { | ||
481 | .fattr = &fattr, | ||
482 | }; | ||
483 | struct nfs_pathconf pathinfo = { | ||
484 | .fattr = &fattr, | ||
485 | }; | ||
486 | int no_root_error = 0; | ||
487 | unsigned long max_rpc_payload; | ||
488 | |||
489 | /* We probably want something more informative here */ | ||
490 | snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
491 | |||
492 | server = NFS_SB(sb); | ||
493 | |||
494 | sb->s_magic = NFS_SUPER_MAGIC; | ||
495 | |||
496 | server->io_stats = nfs_alloc_iostats(); | ||
497 | if (server->io_stats == NULL) | ||
498 | return -ENOMEM; | ||
499 | 452 | ||
500 | root_inode = nfs_get_root(sb, &server->fh, &fsinfo); | 453 | switch (data->version) { |
501 | /* Did getting the root inode fail? */ | 454 | case 1: |
502 | if (IS_ERR(root_inode)) { | 455 | data->namlen = 0; |
503 | no_root_error = PTR_ERR(root_inode); | 456 | case 2: |
504 | goto out_no_root; | 457 | data->bsize = 0; |
458 | case 3: | ||
459 | if (data->flags & NFS_MOUNT_VER3) { | ||
460 | dprintk("%s: mount structure version %d does not support NFSv3\n", | ||
461 | __FUNCTION__, | ||
462 | data->version); | ||
463 | return -EINVAL; | ||
464 | } | ||
465 | data->root.size = NFS2_FHSIZE; | ||
466 | memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); | ||
467 | case 4: | ||
468 | if (data->flags & NFS_MOUNT_SECFLAVOUR) { | ||
469 | dprintk("%s: mount structure version %d does not support strong security\n", | ||
470 | __FUNCTION__, | ||
471 | data->version); | ||
472 | return -EINVAL; | ||
473 | } | ||
474 | /* Fill in pseudoflavor for mount version < 5 */ | ||
475 | data->pseudoflavor = RPC_AUTH_UNIX; | ||
476 | case 5: | ||
477 | memset(data->context, 0, sizeof(data->context)); | ||
505 | } | 478 | } |
506 | sb->s_root = d_alloc_root(root_inode); | 479 | |
507 | if (!sb->s_root) { | 480 | #ifndef CONFIG_NFS_V3 |
508 | no_root_error = -ENOMEM; | 481 | /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ |
509 | goto out_no_root; | 482 | if (data->flags & NFS_MOUNT_VER3) { |
483 | dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); | ||
484 | return -EPROTONOSUPPORT; | ||
510 | } | 485 | } |
511 | sb->s_root->d_op = server->nfs_client->rpc_ops->dentry_ops; | 486 | #endif /* CONFIG_NFS_V3 */ |
512 | |||
513 | /* mount time stamp, in seconds */ | ||
514 | server->mount_time = jiffies; | ||
515 | |||
516 | /* Get some general file system info */ | ||
517 | if (server->namelen == 0 && | ||
518 | server->nfs_client->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) | ||
519 | server->namelen = pathinfo.max_namelen; | ||
520 | /* Work out a lot of parameters */ | ||
521 | if (server->rsize == 0) | ||
522 | server->rsize = nfs_block_size(fsinfo.rtpref, NULL); | ||
523 | if (server->wsize == 0) | ||
524 | server->wsize = nfs_block_size(fsinfo.wtpref, NULL); | ||
525 | |||
526 | if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) | ||
527 | server->rsize = nfs_block_size(fsinfo.rtmax, NULL); | ||
528 | if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax) | ||
529 | server->wsize = nfs_block_size(fsinfo.wtmax, NULL); | ||
530 | |||
531 | max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); | ||
532 | if (server->rsize > max_rpc_payload) | ||
533 | server->rsize = max_rpc_payload; | ||
534 | if (server->rsize > NFS_MAX_FILE_IO_SIZE) | ||
535 | server->rsize = NFS_MAX_FILE_IO_SIZE; | ||
536 | server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
537 | |||
538 | if (server->wsize > max_rpc_payload) | ||
539 | server->wsize = max_rpc_payload; | ||
540 | if (server->wsize > NFS_MAX_FILE_IO_SIZE) | ||
541 | server->wsize = NFS_MAX_FILE_IO_SIZE; | ||
542 | server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
543 | 487 | ||
544 | if (sb->s_blocksize == 0) | 488 | /* We now require that the mount process passes the remote address */ |
545 | sb->s_blocksize = nfs_block_bits(server->wsize, | 489 | if (data->addr.sin_addr.s_addr == INADDR_ANY) { |
546 | &sb->s_blocksize_bits); | 490 | dprintk("%s: mount program didn't pass remote address!\n", |
547 | server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL); | 491 | __FUNCTION__); |
548 | 492 | return -EINVAL; | |
549 | server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); | ||
550 | if (server->dtsize > PAGE_CACHE_SIZE) | ||
551 | server->dtsize = PAGE_CACHE_SIZE; | ||
552 | if (server->dtsize > server->rsize) | ||
553 | server->dtsize = server->rsize; | ||
554 | |||
555 | if (server->flags & NFS_MOUNT_NOAC) { | ||
556 | server->acregmin = server->acregmax = 0; | ||
557 | server->acdirmin = server->acdirmax = 0; | ||
558 | sb->s_flags |= MS_SYNCHRONOUS; | ||
559 | } | 493 | } |
560 | server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; | ||
561 | 494 | ||
562 | nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); | 495 | /* Prepare the root filehandle */ |
496 | if (data->flags & NFS_MOUNT_VER3) | ||
497 | mntfh->size = data->root.size; | ||
498 | else | ||
499 | mntfh->size = NFS2_FHSIZE; | ||
563 | 500 | ||
564 | server->client->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; | 501 | if (mntfh->size > sizeof(mntfh->data)) { |
565 | server->client->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; | 502 | dprintk("%s: invalid root filehandle\n", __FUNCTION__); |
503 | return -EINVAL; | ||
504 | } | ||
505 | |||
506 | memcpy(mntfh->data, data->root.data, mntfh->size); | ||
507 | if (mntfh->size < sizeof(mntfh->data)) | ||
508 | memset(mntfh->data + mntfh->size, 0, | ||
509 | sizeof(mntfh->data) - mntfh->size); | ||
566 | 510 | ||
567 | /* We're airborne Set socket buffersize */ | ||
568 | rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); | ||
569 | return 0; | 511 | return 0; |
570 | /* Yargs. It didn't work out. */ | ||
571 | out_no_root: | ||
572 | dprintk("nfs_sb_init: get root inode failed: errno %d\n", -no_root_error); | ||
573 | if (!IS_ERR(root_inode)) | ||
574 | iput(root_inode); | ||
575 | return no_root_error; | ||
576 | } | 512 | } |
577 | 513 | ||
578 | /* | 514 | /* |
579 | * Create an RPC client handle. | 515 | * Initialise the common bits of the superblock |
580 | */ | 516 | */ |
581 | static struct rpc_clnt * | 517 | static inline void nfs_initialise_sb(struct super_block *sb) |
582 | nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) | ||
583 | { | 518 | { |
584 | struct nfs_client *clp; | 519 | struct nfs_server *server = NFS_SB(sb); |
585 | struct rpc_clnt *clnt; | ||
586 | int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; | ||
587 | int nfsversion = 2; | ||
588 | int err; | ||
589 | |||
590 | #ifdef CONFIG_NFS_V3 | ||
591 | if (server->flags & NFS_MOUNT_VER3) | ||
592 | nfsversion = 3; | ||
593 | #endif | ||
594 | |||
595 | clp = nfs_get_client(server->hostname, &server->addr, nfsversion); | ||
596 | if (!clp) { | ||
597 | dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); | ||
598 | return ERR_PTR(PTR_ERR(clp)); | ||
599 | } | ||
600 | |||
601 | if (clp->cl_cons_state == NFS_CS_INITING) { | ||
602 | /* Check NFS protocol revision and initialize RPC op | ||
603 | * vector and file handle pool. */ | ||
604 | #ifdef CONFIG_NFS_V3 | ||
605 | if (nfsversion == 3) { | ||
606 | clp->rpc_ops = &nfs_v3_clientops; | ||
607 | server->caps |= NFS_CAP_READDIRPLUS; | ||
608 | } else { | ||
609 | clp->rpc_ops = &nfs_v2_clientops; | ||
610 | } | ||
611 | #else | ||
612 | clp->rpc_ops = &nfs_v2_clientops; | ||
613 | #endif | ||
614 | |||
615 | /* create transport and client */ | ||
616 | err = nfs_create_rpc_client(clp, proto, data->timeo, | ||
617 | data->retrans, RPC_AUTH_UNIX); | ||
618 | if (err < 0) | ||
619 | goto client_init_error; | ||
620 | |||
621 | nfs_mark_client_ready(clp, 0); | ||
622 | } | ||
623 | 520 | ||
624 | /* create an nfs_server-specific client */ | 521 | sb->s_magic = NFS_SUPER_MAGIC; |
625 | clnt = rpc_clone_client(clp->cl_rpcclient); | ||
626 | if (IS_ERR(clnt)) { | ||
627 | dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__); | ||
628 | nfs_put_client(clp); | ||
629 | return ERR_PTR(PTR_ERR(clnt)); | ||
630 | } | ||
631 | 522 | ||
632 | if (data->pseudoflavor != clp->cl_rpcclient->cl_auth->au_flavor) { | 523 | /* We probably want something more informative here */ |
633 | struct rpc_auth *auth; | 524 | snprintf(sb->s_id, sizeof(sb->s_id), |
525 | "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
634 | 526 | ||
635 | auth = rpcauth_create(data->pseudoflavor, server->client); | 527 | if (sb->s_blocksize == 0) |
636 | if (IS_ERR(auth)) { | 528 | sb->s_blocksize = nfs_block_bits(server->wsize, |
637 | dprintk("%s: couldn't create credcache!\n", __FUNCTION__); | 529 | &sb->s_blocksize_bits); |
638 | return ERR_PTR(PTR_ERR(auth)); | ||
639 | } | ||
640 | } | ||
641 | 530 | ||
642 | server->nfs_client = clp; | 531 | if (server->flags & NFS_MOUNT_NOAC) |
643 | return clnt; | 532 | sb->s_flags |= MS_SYNCHRONOUS; |
644 | 533 | ||
645 | client_init_error: | 534 | nfs_super_set_maxbytes(sb, server->maxfilesize); |
646 | nfs_mark_client_ready(clp, err); | ||
647 | nfs_put_client(clp); | ||
648 | return ERR_PTR(err); | ||
649 | } | 535 | } |
650 | 536 | ||
651 | /* | 537 | /* |
652 | * Clone a server record | 538 | * Finish setting up an NFS2/3 superblock |
653 | */ | 539 | */ |
654 | static struct nfs_server *nfs_clone_server(struct super_block *sb, struct nfs_clone_mount *data) | 540 | static void nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data) |
655 | { | 541 | { |
656 | struct nfs_server *server = NFS_SB(sb); | 542 | struct nfs_server *server = NFS_SB(sb); |
657 | struct nfs_server *parent = NFS_SB(data->sb); | ||
658 | struct inode *root_inode; | ||
659 | struct nfs_fsinfo fsinfo; | ||
660 | void *err = ERR_PTR(-ENOMEM); | ||
661 | |||
662 | sb->s_op = data->sb->s_op; | ||
663 | sb->s_blocksize = data->sb->s_blocksize; | ||
664 | sb->s_blocksize_bits = data->sb->s_blocksize_bits; | ||
665 | sb->s_maxbytes = data->sb->s_maxbytes; | ||
666 | |||
667 | server->client_acl = ERR_PTR(-EINVAL); | ||
668 | server->io_stats = nfs_alloc_iostats(); | ||
669 | if (server->io_stats == NULL) | ||
670 | goto out; | ||
671 | |||
672 | server->client = rpc_clone_client(parent->client); | ||
673 | if (IS_ERR((err = server->client))) | ||
674 | goto out; | ||
675 | |||
676 | if (!IS_ERR(parent->client_acl)) { | ||
677 | server->client_acl = rpc_clone_client(parent->client_acl); | ||
678 | if (IS_ERR((err = server->client_acl))) | ||
679 | goto out; | ||
680 | } | ||
681 | root_inode = nfs_fhget(sb, data->fh, data->fattr); | ||
682 | if (!root_inode) | ||
683 | goto out; | ||
684 | sb->s_root = d_alloc_root(root_inode); | ||
685 | if (!sb->s_root) | ||
686 | goto out_put_root; | ||
687 | fsinfo.fattr = data->fattr; | ||
688 | if (NFS_PROTO(root_inode)->fsinfo(server, data->fh, &fsinfo) == 0) | ||
689 | nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); | ||
690 | sb->s_root->d_op = server->nfs_client->rpc_ops->dentry_ops; | ||
691 | sb->s_flags |= MS_ACTIVE; | ||
692 | return server; | ||
693 | out_put_root: | ||
694 | iput(root_inode); | ||
695 | out: | ||
696 | return err; | ||
697 | } | ||
698 | |||
699 | /* | ||
700 | * Copy an existing superblock and attach revised data | ||
701 | */ | ||
702 | static int nfs_clone_generic_sb(struct nfs_clone_mount *data, | ||
703 | struct super_block *(*fill_sb)(struct nfs_server *, struct nfs_clone_mount *), | ||
704 | struct nfs_server *(*fill_server)(struct super_block *, struct nfs_clone_mount *), | ||
705 | struct vfsmount *mnt) | ||
706 | { | ||
707 | struct nfs_server *server; | ||
708 | struct nfs_server *parent = NFS_SB(data->sb); | ||
709 | struct super_block *sb = ERR_PTR(-EINVAL); | ||
710 | char *hostname; | ||
711 | int error = -ENOMEM; | ||
712 | int len; | ||
713 | |||
714 | server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL); | ||
715 | if (server == NULL) | ||
716 | goto out_err; | ||
717 | memcpy(server, parent, sizeof(*server)); | ||
718 | atomic_inc(&server->nfs_client->cl_count); | ||
719 | hostname = (data->hostname != NULL) ? data->hostname : parent->hostname; | ||
720 | len = strlen(hostname) + 1; | ||
721 | server->hostname = kmalloc(len, GFP_KERNEL); | ||
722 | if (server->hostname == NULL) | ||
723 | goto free_server; | ||
724 | memcpy(server->hostname, hostname, len); | ||
725 | |||
726 | sb = fill_sb(server, data); | ||
727 | if (IS_ERR(sb)) { | ||
728 | error = PTR_ERR(sb); | ||
729 | goto free_hostname; | ||
730 | } | ||
731 | 543 | ||
732 | if (sb->s_root) | 544 | sb->s_blocksize_bits = 0; |
733 | goto out_share; | 545 | sb->s_blocksize = 0; |
546 | if (data->bsize) | ||
547 | sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); | ||
734 | 548 | ||
735 | server = fill_server(sb, data); | 549 | if (server->flags & NFS_MOUNT_VER3) { |
736 | if (IS_ERR(server)) { | 550 | /* The VFS shouldn't apply the umask to mode bits. We will do |
737 | error = PTR_ERR(server); | 551 | * so ourselves when necessary. |
738 | goto out_deactivate; | 552 | */ |
553 | sb->s_flags |= MS_POSIXACL; | ||
554 | sb->s_time_gran = 1; | ||
739 | } | 555 | } |
740 | return simple_set_mnt(mnt, sb); | 556 | |
741 | out_deactivate: | 557 | sb->s_op = &nfs_sops; |
742 | up_write(&sb->s_umount); | 558 | nfs_initialise_sb(sb); |
743 | deactivate_super(sb); | ||
744 | return error; | ||
745 | out_share: | ||
746 | kfree(server->hostname); | ||
747 | nfs_put_client(server->nfs_client); | ||
748 | kfree(server); | ||
749 | return simple_set_mnt(mnt, sb); | ||
750 | free_hostname: | ||
751 | kfree(server->hostname); | ||
752 | free_server: | ||
753 | nfs_put_client(server->nfs_client); | ||
754 | kfree(server); | ||
755 | out_err: | ||
756 | return error; | ||
757 | } | 559 | } |
758 | 560 | ||
759 | /* | 561 | /* |
760 | * Set up an NFS2/3 superblock | 562 | * Finish setting up a cloned NFS2/3 superblock |
761 | * | ||
762 | * The way this works is that the mount process passes a structure | ||
763 | * in the data argument which contains the server's IP address | ||
764 | * and the root file handle obtained from the server's mount | ||
765 | * daemon. We stash these away in the private superblock fields. | ||
766 | */ | 563 | */ |
767 | static int | 564 | static void nfs_clone_super(struct super_block *sb, |
768 | nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) | 565 | const struct super_block *old_sb) |
769 | { | 566 | { |
770 | struct nfs_server *server; | 567 | struct nfs_server *server = NFS_SB(sb); |
771 | rpc_authflavor_t authflavor; | 568 | |
569 | sb->s_blocksize_bits = old_sb->s_blocksize_bits; | ||
570 | sb->s_blocksize = old_sb->s_blocksize; | ||
571 | sb->s_maxbytes = old_sb->s_maxbytes; | ||
772 | 572 | ||
773 | server = NFS_SB(sb); | ||
774 | sb->s_blocksize_bits = 0; | ||
775 | sb->s_blocksize = 0; | ||
776 | if (data->bsize) | ||
777 | sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); | ||
778 | if (data->rsize) | ||
779 | server->rsize = nfs_block_size(data->rsize, NULL); | ||
780 | if (data->wsize) | ||
781 | server->wsize = nfs_block_size(data->wsize, NULL); | ||
782 | server->flags = data->flags & NFS_MOUNT_FLAGMASK; | ||
783 | |||
784 | server->acregmin = data->acregmin*HZ; | ||
785 | server->acregmax = data->acregmax*HZ; | ||
786 | server->acdirmin = data->acdirmin*HZ; | ||
787 | server->acdirmax = data->acdirmax*HZ; | ||
788 | |||
789 | /* Start lockd here, before we might error out */ | ||
790 | if (!(server->flags & NFS_MOUNT_NONLM)) | ||
791 | lockd_up(); | ||
792 | |||
793 | server->namelen = data->namlen; | ||
794 | server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); | ||
795 | if (!server->hostname) | ||
796 | return -ENOMEM; | ||
797 | strcpy(server->hostname, data->hostname); | ||
798 | |||
799 | /* Fill in pseudoflavor for mount version < 5 */ | ||
800 | if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) | ||
801 | data->pseudoflavor = RPC_AUTH_UNIX; | ||
802 | authflavor = data->pseudoflavor; /* save for sb_init() */ | ||
803 | /* XXX maybe we want to add a server->pseudoflavor field */ | ||
804 | |||
805 | /* Create RPC client handles */ | ||
806 | server->client = nfs_create_client(server, data); | ||
807 | if (IS_ERR(server->client)) | ||
808 | return PTR_ERR(server->client); | ||
809 | |||
810 | /* RFC 2623, sec 2.3.2 */ | ||
811 | if (server->flags & NFS_MOUNT_VER3) { | 573 | if (server->flags & NFS_MOUNT_VER3) { |
812 | #ifdef CONFIG_NFS_V3_ACL | 574 | /* The VFS shouldn't apply the umask to mode bits. We will do |
813 | if (!(server->flags & NFS_MOUNT_NOACL)) { | 575 | * so ourselves when necessary. |
814 | server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); | ||
815 | /* No errors! Assume that Sun nfsacls are supported */ | ||
816 | if (!IS_ERR(server->client_acl)) | ||
817 | server->caps |= NFS_CAP_ACLS; | ||
818 | } | ||
819 | #else | ||
820 | server->flags &= ~NFS_MOUNT_NOACL; | ||
821 | #endif /* CONFIG_NFS_V3_ACL */ | ||
822 | /* | ||
823 | * The VFS shouldn't apply the umask to mode bits. We will | ||
824 | * do so ourselves when necessary. | ||
825 | */ | 576 | */ |
826 | sb->s_flags |= MS_POSIXACL; | 577 | sb->s_flags |= MS_POSIXACL; |
827 | if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) | ||
828 | server->namelen = NFS3_MAXNAMLEN; | ||
829 | sb->s_time_gran = 1; | 578 | sb->s_time_gran = 1; |
830 | } else { | ||
831 | if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) | ||
832 | server->namelen = NFS2_MAXNAMLEN; | ||
833 | } | 579 | } |
834 | 580 | ||
835 | sb->s_op = &nfs_sops; | 581 | sb->s_op = old_sb->s_op; |
836 | return nfs_sb_init(sb, authflavor); | 582 | nfs_initialise_sb(sb); |
837 | } | 583 | } |
838 | 584 | ||
839 | static int nfs_set_super(struct super_block *s, void *data) | 585 | static int nfs_set_super(struct super_block *s, void *_server) |
840 | { | 586 | { |
841 | s->s_fs_info = data; | 587 | struct nfs_server *server = _server; |
842 | return set_anon_super(s, data); | 588 | int ret; |
589 | |||
590 | s->s_fs_info = server; | ||
591 | ret = set_anon_super(s, server); | ||
592 | if (ret == 0) | ||
593 | server->s_dev = s->s_dev; | ||
594 | return ret; | ||
843 | } | 595 | } |
844 | 596 | ||
845 | static int nfs_compare_super(struct super_block *sb, void *data) | 597 | static int nfs_compare_super(struct super_block *sb, void *data) |
846 | { | 598 | { |
847 | struct nfs_server *server = data; | 599 | struct nfs_server *server = data, *old = NFS_SB(sb); |
848 | struct nfs_server *old = NFS_SB(sb); | ||
849 | 600 | ||
850 | if (old->addr.sin_addr.s_addr != server->addr.sin_addr.s_addr) | 601 | if (old->nfs_client != server->nfs_client) |
851 | return 0; | 602 | return 0; |
852 | if (old->addr.sin_port != server->addr.sin_port) | 603 | if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) |
853 | return 0; | 604 | return 0; |
854 | return !nfs_compare_fh(&old->fh, &server->fh); | 605 | return 1; |
855 | } | 606 | } |
856 | 607 | ||
857 | static int nfs_get_sb(struct file_system_type *fs_type, | 608 | static int nfs_get_sb(struct file_system_type *fs_type, |
858 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | 609 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) |
859 | { | 610 | { |
860 | int error; | ||
861 | struct nfs_server *server = NULL; | 611 | struct nfs_server *server = NULL; |
862 | struct super_block *s; | 612 | struct super_block *s; |
863 | struct nfs_fh *root; | 613 | struct nfs_fh mntfh; |
864 | struct nfs_mount_data *data = raw_data; | 614 | struct nfs_mount_data *data = raw_data; |
615 | struct dentry *mntroot; | ||
616 | int error; | ||
865 | 617 | ||
866 | error = -EINVAL; | 618 | /* Validate the mount data */ |
867 | if (data == NULL) { | 619 | error = nfs_validate_mount_data(data, &mntfh); |
868 | dprintk("%s: missing data argument\n", __FUNCTION__); | 620 | if (error < 0) |
869 | goto out_err_noserver; | 621 | return error; |
870 | } | ||
871 | if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { | ||
872 | dprintk("%s: bad mount version\n", __FUNCTION__); | ||
873 | goto out_err_noserver; | ||
874 | } | ||
875 | switch (data->version) { | ||
876 | case 1: | ||
877 | data->namlen = 0; | ||
878 | case 2: | ||
879 | data->bsize = 0; | ||
880 | case 3: | ||
881 | if (data->flags & NFS_MOUNT_VER3) { | ||
882 | dprintk("%s: mount structure version %d does not support NFSv3\n", | ||
883 | __FUNCTION__, | ||
884 | data->version); | ||
885 | goto out_err_noserver; | ||
886 | } | ||
887 | data->root.size = NFS2_FHSIZE; | ||
888 | memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); | ||
889 | case 4: | ||
890 | if (data->flags & NFS_MOUNT_SECFLAVOUR) { | ||
891 | dprintk("%s: mount structure version %d does not support strong security\n", | ||
892 | __FUNCTION__, | ||
893 | data->version); | ||
894 | goto out_err_noserver; | ||
895 | } | ||
896 | case 5: | ||
897 | memset(data->context, 0, sizeof(data->context)); | ||
898 | } | ||
899 | #ifndef CONFIG_NFS_V3 | ||
900 | /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ | ||
901 | error = -EPROTONOSUPPORT; | ||
902 | if (data->flags & NFS_MOUNT_VER3) { | ||
903 | dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); | ||
904 | goto out_err_noserver; | ||
905 | } | ||
906 | #endif /* CONFIG_NFS_V3 */ | ||
907 | 622 | ||
908 | error = -ENOMEM; | 623 | /* Get a volume representation */ |
909 | server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); | 624 | server = nfs_create_server(data, &mntfh); |
910 | if (!server) | 625 | if (IS_ERR(server)) { |
626 | error = PTR_ERR(server); | ||
911 | goto out_err_noserver; | 627 | goto out_err_noserver; |
912 | /* Zero out the NFS state stuff */ | ||
913 | init_nfsv4_state(server); | ||
914 | server->client = server->client_acl = ERR_PTR(-EINVAL); | ||
915 | |||
916 | root = &server->fh; | ||
917 | if (data->flags & NFS_MOUNT_VER3) | ||
918 | root->size = data->root.size; | ||
919 | else | ||
920 | root->size = NFS2_FHSIZE; | ||
921 | error = -EINVAL; | ||
922 | if (root->size > sizeof(root->data)) { | ||
923 | dprintk("%s: invalid root filehandle\n", __FUNCTION__); | ||
924 | goto out_err; | ||
925 | } | ||
926 | memcpy(root->data, data->root.data, root->size); | ||
927 | |||
928 | /* We now require that the mount process passes the remote address */ | ||
929 | memcpy(&server->addr, &data->addr, sizeof(server->addr)); | ||
930 | if (server->addr.sin_addr.s_addr == INADDR_ANY) { | ||
931 | dprintk("%s: mount program didn't pass remote address!\n", | ||
932 | __FUNCTION__); | ||
933 | goto out_err; | ||
934 | } | 628 | } |
935 | 629 | ||
630 | /* Get a superblock - note that we may end up sharing one that already exists */ | ||
936 | s = sget(fs_type, nfs_compare_super, nfs_set_super, server); | 631 | s = sget(fs_type, nfs_compare_super, nfs_set_super, server); |
937 | if (IS_ERR(s)) { | 632 | if (IS_ERR(s)) { |
938 | error = PTR_ERR(s); | 633 | error = PTR_ERR(s); |
939 | goto out_err; | 634 | goto out_err_nosb; |
940 | } | 635 | } |
941 | 636 | ||
942 | if (s->s_root) | 637 | if (s->s_fs_info != server) { |
943 | goto out_share; | 638 | nfs_free_server(server); |
639 | server = NULL; | ||
640 | } | ||
944 | 641 | ||
945 | s->s_flags = flags; | 642 | if (!s->s_root) { |
643 | /* initial superblock/root creation */ | ||
644 | s->s_flags = flags; | ||
645 | nfs_fill_super(s, data); | ||
646 | } | ||
946 | 647 | ||
947 | error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0); | 648 | mntroot = nfs_get_root(s, &mntfh); |
948 | if (error) { | 649 | if (IS_ERR(mntroot)) { |
949 | up_write(&s->s_umount); | 650 | error = PTR_ERR(mntroot); |
950 | deactivate_super(s); | 651 | goto error_splat_super; |
951 | return error; | ||
952 | } | 652 | } |
953 | s->s_flags |= MS_ACTIVE; | ||
954 | return simple_set_mnt(mnt, s); | ||
955 | 653 | ||
956 | out_share: | 654 | s->s_flags |= MS_ACTIVE; |
957 | kfree(server); | 655 | mnt->mnt_sb = s; |
958 | return simple_set_mnt(mnt, s); | 656 | mnt->mnt_root = mntroot; |
657 | return 0; | ||
959 | 658 | ||
960 | out_err: | 659 | out_err_nosb: |
961 | kfree(server); | 660 | nfs_free_server(server); |
962 | out_err_noserver: | 661 | out_err_noserver: |
963 | return error; | 662 | return error; |
663 | |||
664 | error_splat_super: | ||
665 | up_write(&s->s_umount); | ||
666 | deactivate_super(s); | ||
667 | return error; | ||
964 | } | 668 | } |
965 | 669 | ||
670 | /* | ||
671 | * Destroy an NFS2/3 superblock | ||
672 | */ | ||
966 | static void nfs_kill_super(struct super_block *s) | 673 | static void nfs_kill_super(struct super_block *s) |
967 | { | 674 | { |
968 | struct nfs_server *server = NFS_SB(s); | 675 | struct nfs_server *server = NFS_SB(s); |
969 | 676 | ||
970 | kill_anon_super(s); | 677 | kill_anon_super(s); |
971 | 678 | nfs_free_server(server); | |
972 | if (!IS_ERR(server->client)) | ||
973 | rpc_shutdown_client(server->client); | ||
974 | if (!IS_ERR(server->client_acl)) | ||
975 | rpc_shutdown_client(server->client_acl); | ||
976 | |||
977 | if (!(server->flags & NFS_MOUNT_NONLM)) | ||
978 | lockd_down(); /* release rpc.lockd */ | ||
979 | |||
980 | nfs_free_iostats(server->io_stats); | ||
981 | kfree(server->hostname); | ||
982 | nfs_put_client(server->nfs_client); | ||
983 | kfree(server); | ||
984 | nfs_release_automount_timer(); | ||
985 | } | 679 | } |
986 | 680 | ||
987 | static struct super_block *nfs_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data) | 681 | /* |
988 | { | 682 | * Clone an NFS2/3 server record on xdev traversal (FSID-change) |
989 | struct super_block *sb; | 683 | */ |
990 | 684 | static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, | |
991 | server->fsid = data->fattr->fsid; | 685 | const char *dev_name, void *raw_data, |
992 | nfs_copy_fh(&server->fh, data->fh); | 686 | struct vfsmount *mnt) |
993 | sb = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); | ||
994 | if (!IS_ERR(sb) && sb->s_root == NULL && !(server->flags & NFS_MOUNT_NONLM)) | ||
995 | lockd_up(); | ||
996 | return sb; | ||
997 | } | ||
998 | |||
999 | static int nfs_clone_nfs_sb(struct file_system_type *fs_type, | ||
1000 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | ||
1001 | { | 687 | { |
1002 | struct nfs_clone_mount *data = raw_data; | 688 | struct nfs_clone_mount *data = raw_data; |
1003 | return nfs_clone_generic_sb(data, nfs_clone_sb, nfs_clone_server, mnt); | 689 | struct super_block *s; |
1004 | } | 690 | struct nfs_server *server; |
691 | struct dentry *mntroot; | ||
692 | int error; | ||
1005 | 693 | ||
1006 | #ifdef CONFIG_NFS_V4 | 694 | dprintk("--> nfs_xdev_get_sb()\n"); |
1007 | static struct rpc_clnt *nfs4_create_client(struct nfs_server *server, | ||
1008 | int timeo, int retrans, int proto, rpc_authflavor_t flavor) | ||
1009 | { | ||
1010 | struct nfs_client *clp; | ||
1011 | struct rpc_clnt *clnt = NULL; | ||
1012 | int err = -EIO; | ||
1013 | |||
1014 | clp = nfs_get_client(server->hostname, &server->addr, 4); | ||
1015 | if (!clp) { | ||
1016 | dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); | ||
1017 | return ERR_PTR(err); | ||
1018 | } | ||
1019 | 695 | ||
1020 | /* Now create transport and client */ | 696 | /* create a new volume representation */ |
1021 | if (clp->cl_cons_state == NFS_CS_INITING) { | 697 | server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr); |
1022 | clp->rpc_ops = &nfs_v4_clientops; | 698 | if (IS_ERR(server)) { |
699 | error = PTR_ERR(server); | ||
700 | goto out_err_noserver; | ||
701 | } | ||
1023 | 702 | ||
1024 | err = nfs_create_rpc_client(clp, proto, timeo, retrans, flavor); | 703 | /* Get a superblock - note that we may end up sharing one that already exists */ |
1025 | if (err < 0) | 704 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); |
1026 | goto client_init_error; | 705 | if (IS_ERR(s)) { |
706 | error = PTR_ERR(s); | ||
707 | goto out_err_nosb; | ||
708 | } | ||
1027 | 709 | ||
1028 | memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); | 710 | if (s->s_fs_info != server) { |
1029 | err = nfs_idmap_new(clp); | 711 | nfs_free_server(server); |
1030 | if (err < 0) { | 712 | server = NULL; |
1031 | dprintk("%s: failed to create idmapper.\n", | ||
1032 | __FUNCTION__); | ||
1033 | goto client_init_error; | ||
1034 | } | ||
1035 | __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); | ||
1036 | nfs_mark_client_ready(clp, 0); | ||
1037 | } | 713 | } |
1038 | 714 | ||
1039 | clnt = rpc_clone_client(clp->cl_rpcclient); | 715 | if (!s->s_root) { |
716 | /* initial superblock/root creation */ | ||
717 | s->s_flags = flags; | ||
718 | nfs_clone_super(s, data->sb); | ||
719 | } | ||
1040 | 720 | ||
1041 | if (IS_ERR(clnt)) { | 721 | mntroot = nfs_get_root(s, data->fh); |
1042 | dprintk("%s: cannot create RPC client. Error = %d\n", | 722 | if (IS_ERR(mntroot)) { |
1043 | __FUNCTION__, err); | 723 | error = PTR_ERR(mntroot); |
1044 | return clnt; | 724 | goto error_splat_super; |
1045 | } | 725 | } |
1046 | 726 | ||
1047 | if (clnt->cl_auth->au_flavor != flavor) { | 727 | s->s_flags |= MS_ACTIVE; |
1048 | struct rpc_auth *auth; | 728 | mnt->mnt_sb = s; |
729 | mnt->mnt_root = mntroot; | ||
1049 | 730 | ||
1050 | auth = rpcauth_create(flavor, clnt); | 731 | dprintk("<-- nfs_xdev_get_sb() = 0\n"); |
1051 | if (IS_ERR(auth)) { | 732 | return 0; |
1052 | dprintk("%s: couldn't create credcache!\n", __FUNCTION__); | ||
1053 | return (struct rpc_clnt *)auth; | ||
1054 | } | ||
1055 | } | ||
1056 | 733 | ||
1057 | server->nfs_client = clp; | 734 | out_err_nosb: |
1058 | down_write(&clp->cl_sem); | 735 | nfs_free_server(server); |
1059 | list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); | 736 | out_err_noserver: |
1060 | up_write(&clp->cl_sem); | 737 | dprintk("<-- nfs_xdev_get_sb() = %d [error]\n", error); |
1061 | return clnt; | 738 | return error; |
1062 | 739 | ||
1063 | client_init_error: | 740 | error_splat_super: |
1064 | nfs_mark_client_ready(clp, err); | 741 | up_write(&s->s_umount); |
1065 | nfs_put_client(clp); | 742 | deactivate_super(s); |
1066 | return ERR_PTR(err); | 743 | dprintk("<-- nfs_xdev_get_sb() = %d [splat]\n", error); |
744 | return error; | ||
1067 | } | 745 | } |
1068 | 746 | ||
747 | #ifdef CONFIG_NFS_V4 | ||
748 | |||
1069 | /* | 749 | /* |
1070 | * Set up an NFS4 superblock | 750 | * Finish setting up a cloned NFS4 superblock |
1071 | */ | 751 | */ |
1072 | static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) | 752 | static void nfs4_clone_super(struct super_block *sb, |
753 | const struct super_block *old_sb) | ||
1073 | { | 754 | { |
1074 | struct nfs_server *server; | 755 | sb->s_blocksize_bits = old_sb->s_blocksize_bits; |
1075 | rpc_authflavor_t authflavour; | 756 | sb->s_blocksize = old_sb->s_blocksize; |
1076 | int err = -EIO; | 757 | sb->s_maxbytes = old_sb->s_maxbytes; |
1077 | |||
1078 | sb->s_blocksize_bits = 0; | ||
1079 | sb->s_blocksize = 0; | ||
1080 | server = NFS_SB(sb); | ||
1081 | if (data->rsize != 0) | ||
1082 | server->rsize = nfs_block_size(data->rsize, NULL); | ||
1083 | if (data->wsize != 0) | ||
1084 | server->wsize = nfs_block_size(data->wsize, NULL); | ||
1085 | server->flags = data->flags & NFS_MOUNT_FLAGMASK; | ||
1086 | server->caps = NFS_CAP_ATOMIC_OPEN; | ||
1087 | |||
1088 | server->acregmin = data->acregmin*HZ; | ||
1089 | server->acregmax = data->acregmax*HZ; | ||
1090 | server->acdirmin = data->acdirmin*HZ; | ||
1091 | server->acdirmax = data->acdirmax*HZ; | ||
1092 | |||
1093 | /* Now create transport and client */ | ||
1094 | authflavour = RPC_AUTH_UNIX; | ||
1095 | if (data->auth_flavourlen != 0) { | ||
1096 | if (data->auth_flavourlen != 1) { | ||
1097 | dprintk("%s: Invalid number of RPC auth flavours %d.\n", | ||
1098 | __FUNCTION__, data->auth_flavourlen); | ||
1099 | err = -EINVAL; | ||
1100 | goto out_fail; | ||
1101 | } | ||
1102 | if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) { | ||
1103 | err = -EFAULT; | ||
1104 | goto out_fail; | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | server->client = nfs4_create_client(server, data->timeo, data->retrans, | ||
1109 | data->proto, authflavour); | ||
1110 | if (IS_ERR(server->client)) { | ||
1111 | err = PTR_ERR(server->client); | ||
1112 | dprintk("%s: cannot create RPC client. Error = %d\n", | ||
1113 | __FUNCTION__, err); | ||
1114 | goto out_fail; | ||
1115 | } | ||
1116 | |||
1117 | sb->s_time_gran = 1; | 758 | sb->s_time_gran = 1; |
1118 | 759 | sb->s_op = old_sb->s_op; | |
1119 | sb->s_op = &nfs4_sops; | 760 | nfs_initialise_sb(sb); |
1120 | err = nfs_sb_init(sb, authflavour); | ||
1121 | |||
1122 | out_fail: | ||
1123 | return err; | ||
1124 | } | 761 | } |
1125 | 762 | ||
1126 | static int nfs4_compare_super(struct super_block *sb, void *data) | 763 | /* |
764 | * Set up an NFS4 superblock | ||
765 | */ | ||
766 | static void nfs4_fill_super(struct super_block *sb) | ||
1127 | { | 767 | { |
1128 | struct nfs_server *server = data; | 768 | sb->s_time_gran = 1; |
1129 | struct nfs_server *old = NFS_SB(sb); | 769 | sb->s_op = &nfs4_sops; |
1130 | 770 | nfs_initialise_sb(sb); | |
1131 | if (strcmp(server->hostname, old->hostname) != 0) | ||
1132 | return 0; | ||
1133 | if (strcmp(server->mnt_path, old->mnt_path) != 0) | ||
1134 | return 0; | ||
1135 | return 1; | ||
1136 | } | 771 | } |
1137 | 772 | ||
1138 | static void * | 773 | static void *nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) |
1139 | nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) | ||
1140 | { | 774 | { |
1141 | void *p = NULL; | 775 | void *p = NULL; |
1142 | 776 | ||
@@ -1157,14 +791,22 @@ nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) | |||
1157 | return dst; | 791 | return dst; |
1158 | } | 792 | } |
1159 | 793 | ||
794 | /* | ||
795 | * Get the superblock for an NFS4 mountpoint | ||
796 | */ | ||
1160 | static int nfs4_get_sb(struct file_system_type *fs_type, | 797 | static int nfs4_get_sb(struct file_system_type *fs_type, |
1161 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | 798 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) |
1162 | { | 799 | { |
1163 | int error; | ||
1164 | struct nfs_server *server; | ||
1165 | struct super_block *s; | ||
1166 | struct nfs4_mount_data *data = raw_data; | 800 | struct nfs4_mount_data *data = raw_data; |
801 | struct super_block *s; | ||
802 | struct nfs_server *server; | ||
803 | struct sockaddr_in addr; | ||
804 | rpc_authflavor_t authflavour; | ||
805 | struct nfs_fh mntfh; | ||
806 | struct dentry *mntroot; | ||
807 | char *mntpath = NULL, *hostname = NULL, ip_addr[16]; | ||
1167 | void *p; | 808 | void *p; |
809 | int error; | ||
1168 | 810 | ||
1169 | if (data == NULL) { | 811 | if (data == NULL) { |
1170 | dprintk("%s: missing data argument\n", __FUNCTION__); | 812 | dprintk("%s: missing data argument\n", __FUNCTION__); |
@@ -1175,75 +817,107 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
1175 | return -EINVAL; | 817 | return -EINVAL; |
1176 | } | 818 | } |
1177 | 819 | ||
1178 | server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); | 820 | /* We now require that the mount process passes the remote address */ |
1179 | if (!server) | 821 | if (data->host_addrlen != sizeof(addr)) |
1180 | return -ENOMEM; | 822 | return -EINVAL; |
1181 | /* Zero out the NFS state stuff */ | 823 | |
1182 | init_nfsv4_state(server); | 824 | if (copy_from_user(&addr, data->host_addr, sizeof(addr))) |
1183 | server->client = server->client_acl = ERR_PTR(-EINVAL); | 825 | return -EFAULT; |
826 | |||
827 | if (addr.sin_family != AF_INET || | ||
828 | addr.sin_addr.s_addr == INADDR_ANY | ||
829 | ) { | ||
830 | dprintk("%s: mount program didn't pass remote IP address!\n", | ||
831 | __FUNCTION__); | ||
832 | return -EINVAL; | ||
833 | } | ||
834 | |||
835 | /* Grab the authentication type */ | ||
836 | authflavour = RPC_AUTH_UNIX; | ||
837 | if (data->auth_flavourlen != 0) { | ||
838 | if (data->auth_flavourlen != 1) { | ||
839 | dprintk("%s: Invalid number of RPC auth flavours %d.\n", | ||
840 | __FUNCTION__, data->auth_flavourlen); | ||
841 | error = -EINVAL; | ||
842 | goto out_err_noserver; | ||
843 | } | ||
844 | |||
845 | if (copy_from_user(&authflavour, data->auth_flavours, | ||
846 | sizeof(authflavour))) { | ||
847 | error = -EFAULT; | ||
848 | goto out_err_noserver; | ||
849 | } | ||
850 | } | ||
1184 | 851 | ||
1185 | p = nfs_copy_user_string(NULL, &data->hostname, 256); | 852 | p = nfs_copy_user_string(NULL, &data->hostname, 256); |
1186 | if (IS_ERR(p)) | 853 | if (IS_ERR(p)) |
1187 | goto out_err; | 854 | goto out_err; |
1188 | server->hostname = p; | 855 | hostname = p; |
1189 | 856 | ||
1190 | p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); | 857 | p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); |
1191 | if (IS_ERR(p)) | 858 | if (IS_ERR(p)) |
1192 | goto out_err; | 859 | goto out_err; |
1193 | server->mnt_path = p; | 860 | mntpath = p; |
1194 | 861 | ||
1195 | p = nfs_copy_user_string(server->ip_addr, &data->client_addr, | 862 | dprintk("MNTPATH: %s\n", mntpath); |
1196 | sizeof(server->ip_addr) - 1); | 863 | |
864 | p = nfs_copy_user_string(ip_addr, &data->client_addr, | ||
865 | sizeof(ip_addr) - 1); | ||
1197 | if (IS_ERR(p)) | 866 | if (IS_ERR(p)) |
1198 | goto out_err; | 867 | goto out_err; |
1199 | 868 | ||
1200 | /* We now require that the mount process passes the remote address */ | 869 | /* Get a volume representation */ |
1201 | if (data->host_addrlen != sizeof(server->addr)) { | 870 | server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr, |
1202 | error = -EINVAL; | 871 | authflavour, &mntfh); |
1203 | goto out_free; | 872 | if (IS_ERR(server)) { |
1204 | } | 873 | error = PTR_ERR(server); |
1205 | if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) { | 874 | goto out_err_noserver; |
1206 | error = -EFAULT; | ||
1207 | goto out_free; | ||
1208 | } | ||
1209 | if (server->addr.sin_family != AF_INET || | ||
1210 | server->addr.sin_addr.s_addr == INADDR_ANY) { | ||
1211 | dprintk("%s: mount program didn't pass remote IP address!\n", | ||
1212 | __FUNCTION__); | ||
1213 | error = -EINVAL; | ||
1214 | goto out_free; | ||
1215 | } | 875 | } |
1216 | 876 | ||
1217 | s = sget(fs_type, nfs4_compare_super, nfs_set_super, server); | 877 | /* Get a superblock - note that we may end up sharing one that already exists */ |
878 | s = sget(fs_type, nfs_compare_super, nfs_set_super, server); | ||
1218 | if (IS_ERR(s)) { | 879 | if (IS_ERR(s)) { |
1219 | error = PTR_ERR(s); | 880 | error = PTR_ERR(s); |
1220 | goto out_free; | 881 | goto out_free; |
1221 | } | 882 | } |
1222 | 883 | ||
1223 | if (s->s_root) { | 884 | if (!s->s_root) { |
1224 | kfree(server->mnt_path); | 885 | /* initial superblock/root creation */ |
1225 | kfree(server->hostname); | 886 | s->s_flags = flags; |
1226 | kfree(server); | ||
1227 | return simple_set_mnt(mnt, s); | ||
1228 | } | ||
1229 | 887 | ||
1230 | s->s_flags = flags; | 888 | nfs4_fill_super(s); |
889 | } else { | ||
890 | nfs_free_server(server); | ||
891 | } | ||
1231 | 892 | ||
1232 | error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0); | 893 | mntroot = nfs4_get_root(s, &mntfh); |
1233 | if (error) { | 894 | if (IS_ERR(mntroot)) { |
1234 | up_write(&s->s_umount); | 895 | error = PTR_ERR(mntroot); |
1235 | deactivate_super(s); | 896 | goto error_splat_super; |
1236 | return error; | ||
1237 | } | 897 | } |
898 | |||
1238 | s->s_flags |= MS_ACTIVE; | 899 | s->s_flags |= MS_ACTIVE; |
1239 | return simple_set_mnt(mnt, s); | 900 | mnt->mnt_sb = s; |
901 | mnt->mnt_root = mntroot; | ||
902 | kfree(mntpath); | ||
903 | kfree(hostname); | ||
904 | return 0; | ||
905 | |||
1240 | out_err: | 906 | out_err: |
1241 | error = PTR_ERR(p); | 907 | error = PTR_ERR(p); |
908 | goto out_err_noserver; | ||
909 | |||
1242 | out_free: | 910 | out_free: |
1243 | kfree(server->mnt_path); | 911 | nfs_free_server(server); |
1244 | kfree(server->hostname); | 912 | out_err_noserver: |
1245 | kfree(server); | 913 | kfree(mntpath); |
914 | kfree(hostname); | ||
1246 | return error; | 915 | return error; |
916 | |||
917 | error_splat_super: | ||
918 | up_write(&s->s_umount); | ||
919 | deactivate_super(s); | ||
920 | goto out_err_noserver; | ||
1247 | } | 921 | } |
1248 | 922 | ||
1249 | static void nfs4_kill_super(struct super_block *sb) | 923 | static void nfs4_kill_super(struct super_block *sb) |
@@ -1254,133 +928,140 @@ static void nfs4_kill_super(struct super_block *sb) | |||
1254 | kill_anon_super(sb); | 928 | kill_anon_super(sb); |
1255 | 929 | ||
1256 | nfs4_renewd_prepare_shutdown(server); | 930 | nfs4_renewd_prepare_shutdown(server); |
1257 | 931 | nfs_free_server(server); | |
1258 | if (server->client != NULL && !IS_ERR(server->client)) | ||
1259 | rpc_shutdown_client(server->client); | ||
1260 | |||
1261 | destroy_nfsv4_state(server); | ||
1262 | |||
1263 | nfs_free_iostats(server->io_stats); | ||
1264 | kfree(server->hostname); | ||
1265 | kfree(server); | ||
1266 | nfs_release_automount_timer(); | ||
1267 | } | 932 | } |
1268 | 933 | ||
1269 | /* | 934 | /* |
1270 | * Constructs the SERVER-side path | 935 | * Clone an NFS4 server record on xdev traversal (FSID-change) |
1271 | */ | 936 | */ |
1272 | static inline char *nfs4_dup_path(const struct dentry *dentry) | 937 | static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, |
938 | const char *dev_name, void *raw_data, | ||
939 | struct vfsmount *mnt) | ||
1273 | { | 940 | { |
1274 | char *page = (char *) __get_free_page(GFP_USER); | 941 | struct nfs_clone_mount *data = raw_data; |
1275 | char *path; | 942 | struct super_block *s; |
943 | struct nfs_server *server; | ||
944 | struct dentry *mntroot; | ||
945 | int error; | ||
1276 | 946 | ||
1277 | path = nfs4_path(dentry, page, PAGE_SIZE); | 947 | dprintk("--> nfs4_xdev_get_sb()\n"); |
1278 | if (!IS_ERR(path)) { | ||
1279 | int len = PAGE_SIZE + page - path; | ||
1280 | char *tmp = path; | ||
1281 | 948 | ||
1282 | path = kmalloc(len, GFP_KERNEL); | 949 | /* create a new volume representation */ |
1283 | if (path) | 950 | server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr); |
1284 | memcpy(path, tmp, len); | 951 | if (IS_ERR(server)) { |
1285 | else | 952 | error = PTR_ERR(server); |
1286 | path = ERR_PTR(-ENOMEM); | 953 | goto out_err_noserver; |
1287 | } | 954 | } |
1288 | free_page((unsigned long)page); | ||
1289 | return path; | ||
1290 | } | ||
1291 | 955 | ||
1292 | static struct super_block *nfs4_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data) | 956 | /* Get a superblock - note that we may end up sharing one that already exists */ |
1293 | { | 957 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); |
1294 | const struct dentry *dentry = data->dentry; | 958 | if (IS_ERR(s)) { |
1295 | struct nfs_client *clp = server->nfs_client; | 959 | error = PTR_ERR(s); |
1296 | struct super_block *sb; | 960 | goto out_err_nosb; |
1297 | |||
1298 | server->fsid = data->fattr->fsid; | ||
1299 | nfs_copy_fh(&server->fh, data->fh); | ||
1300 | server->mnt_path = nfs4_dup_path(dentry); | ||
1301 | if (IS_ERR(server->mnt_path)) { | ||
1302 | sb = (struct super_block *)server->mnt_path; | ||
1303 | goto err; | ||
1304 | } | 961 | } |
1305 | sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server); | ||
1306 | if (IS_ERR(sb) || sb->s_root) | ||
1307 | goto free_path; | ||
1308 | nfs4_server_capabilities(server, &server->fh); | ||
1309 | |||
1310 | down_write(&clp->cl_sem); | ||
1311 | list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); | ||
1312 | up_write(&clp->cl_sem); | ||
1313 | return sb; | ||
1314 | free_path: | ||
1315 | kfree(server->mnt_path); | ||
1316 | err: | ||
1317 | server->mnt_path = NULL; | ||
1318 | return sb; | ||
1319 | } | ||
1320 | 962 | ||
1321 | static int nfs_clone_nfs4_sb(struct file_system_type *fs_type, | 963 | if (s->s_fs_info != server) { |
1322 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | 964 | nfs_free_server(server); |
1323 | { | 965 | server = NULL; |
1324 | struct nfs_clone_mount *data = raw_data; | 966 | } |
1325 | return nfs_clone_generic_sb(data, nfs4_clone_sb, nfs_clone_server, mnt); | ||
1326 | } | ||
1327 | 967 | ||
1328 | static struct super_block *nfs4_referral_sb(struct nfs_server *server, struct nfs_clone_mount *data) | 968 | if (!s->s_root) { |
1329 | { | 969 | /* initial superblock/root creation */ |
1330 | struct super_block *sb = ERR_PTR(-ENOMEM); | 970 | s->s_flags = flags; |
1331 | int len; | 971 | nfs4_clone_super(s, data->sb); |
1332 | 972 | } | |
1333 | len = strlen(data->mnt_path) + 1; | ||
1334 | server->mnt_path = kmalloc(len, GFP_KERNEL); | ||
1335 | if (server->mnt_path == NULL) | ||
1336 | goto err; | ||
1337 | memcpy(server->mnt_path, data->mnt_path, len); | ||
1338 | memcpy(&server->addr, data->addr, sizeof(struct sockaddr_in)); | ||
1339 | |||
1340 | sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server); | ||
1341 | if (IS_ERR(sb) || sb->s_root) | ||
1342 | goto free_path; | ||
1343 | return sb; | ||
1344 | free_path: | ||
1345 | kfree(server->mnt_path); | ||
1346 | err: | ||
1347 | server->mnt_path = NULL; | ||
1348 | return sb; | ||
1349 | } | ||
1350 | 973 | ||
1351 | static struct nfs_server *nfs4_referral_server(struct super_block *sb, struct nfs_clone_mount *data) | 974 | mntroot = nfs4_get_root(s, data->fh); |
1352 | { | 975 | if (IS_ERR(mntroot)) { |
1353 | struct nfs_server *server = NFS_SB(sb); | 976 | error = PTR_ERR(mntroot); |
1354 | int proto, timeo, retrans; | 977 | goto error_splat_super; |
1355 | void *err; | 978 | } |
1356 | |||
1357 | proto = IPPROTO_TCP; | ||
1358 | /* Since we are following a referral and there may be alternatives, | ||
1359 | set the timeouts and retries to low values */ | ||
1360 | timeo = 2; | ||
1361 | retrans = 1; | ||
1362 | |||
1363 | nfs_put_client(server->nfs_client); | ||
1364 | server->nfs_client = NULL; | ||
1365 | server->client = nfs4_create_client(server, timeo, retrans, proto, | ||
1366 | data->authflavor); | ||
1367 | if (IS_ERR((err = server->client))) | ||
1368 | goto out_err; | ||
1369 | 979 | ||
1370 | sb->s_time_gran = 1; | 980 | s->s_flags |= MS_ACTIVE; |
1371 | sb->s_op = &nfs4_sops; | 981 | mnt->mnt_sb = s; |
1372 | err = ERR_PTR(nfs_sb_init(sb, data->authflavor)); | 982 | mnt->mnt_root = mntroot; |
1373 | if (!IS_ERR(err)) | 983 | |
1374 | return server; | 984 | dprintk("<-- nfs4_xdev_get_sb() = 0\n"); |
1375 | out_err: | 985 | return 0; |
1376 | return (struct nfs_server *)err; | 986 | |
987 | out_err_nosb: | ||
988 | nfs_free_server(server); | ||
989 | out_err_noserver: | ||
990 | dprintk("<-- nfs4_xdev_get_sb() = %d [error]\n", error); | ||
991 | return error; | ||
992 | |||
993 | error_splat_super: | ||
994 | up_write(&s->s_umount); | ||
995 | deactivate_super(s); | ||
996 | dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error); | ||
997 | return error; | ||
1377 | } | 998 | } |
1378 | 999 | ||
1379 | static int nfs_referral_nfs4_sb(struct file_system_type *fs_type, | 1000 | /* |
1380 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | 1001 | * Create an NFS4 server record on referral traversal |
1002 | */ | ||
1003 | static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | ||
1004 | const char *dev_name, void *raw_data, | ||
1005 | struct vfsmount *mnt) | ||
1381 | { | 1006 | { |
1382 | struct nfs_clone_mount *data = raw_data; | 1007 | struct nfs_clone_mount *data = raw_data; |
1383 | return nfs_clone_generic_sb(data, nfs4_referral_sb, nfs4_referral_server, mnt); | 1008 | struct super_block *s; |
1009 | struct nfs_server *server; | ||
1010 | struct dentry *mntroot; | ||
1011 | struct nfs_fh mntfh; | ||
1012 | int error; | ||
1013 | |||
1014 | dprintk("--> nfs4_referral_get_sb()\n"); | ||
1015 | |||
1016 | /* create a new volume representation */ | ||
1017 | server = nfs4_create_referral_server(data, &mntfh); | ||
1018 | if (IS_ERR(server)) { | ||
1019 | error = PTR_ERR(server); | ||
1020 | goto out_err_noserver; | ||
1021 | } | ||
1022 | |||
1023 | /* Get a superblock - note that we may end up sharing one that already exists */ | ||
1024 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); | ||
1025 | if (IS_ERR(s)) { | ||
1026 | error = PTR_ERR(s); | ||
1027 | goto out_err_nosb; | ||
1028 | } | ||
1029 | |||
1030 | if (s->s_fs_info != server) { | ||
1031 | nfs_free_server(server); | ||
1032 | server = NULL; | ||
1033 | } | ||
1034 | |||
1035 | if (!s->s_root) { | ||
1036 | /* initial superblock/root creation */ | ||
1037 | s->s_flags = flags; | ||
1038 | nfs4_fill_super(s); | ||
1039 | } | ||
1040 | |||
1041 | mntroot = nfs4_get_root(s, data->fh); | ||
1042 | if (IS_ERR(mntroot)) { | ||
1043 | error = PTR_ERR(mntroot); | ||
1044 | goto error_splat_super; | ||
1045 | } | ||
1046 | |||
1047 | s->s_flags |= MS_ACTIVE; | ||
1048 | mnt->mnt_sb = s; | ||
1049 | mnt->mnt_root = mntroot; | ||
1050 | |||
1051 | dprintk("<-- nfs4_referral_get_sb() = 0\n"); | ||
1052 | return 0; | ||
1053 | |||
1054 | out_err_nosb: | ||
1055 | nfs_free_server(server); | ||
1056 | out_err_noserver: | ||
1057 | dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error); | ||
1058 | return error; | ||
1059 | |||
1060 | error_splat_super: | ||
1061 | up_write(&s->s_umount); | ||
1062 | deactivate_super(s); | ||
1063 | dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error); | ||
1064 | return error; | ||
1384 | } | 1065 | } |
1385 | 1066 | ||
1386 | #endif | 1067 | #endif /* CONFIG_NFS_V4 */ |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 7084ac9a6455..453d44666ea5 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -1273,7 +1273,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1273 | if (time_before(complain, jiffies)) { | 1273 | if (time_before(complain, jiffies)) { |
1274 | dprintk("NFS: faulty NFS server %s:" | 1274 | dprintk("NFS: faulty NFS server %s:" |
1275 | " (committed = %d) != (stable = %d)\n", | 1275 | " (committed = %d) != (stable = %d)\n", |
1276 | NFS_SERVER(data->inode)->hostname, | 1276 | NFS_SERVER(data->inode)->nfs_client->cl_hostname, |
1277 | resp->verf->committed, argp->stable); | 1277 | resp->verf->committed, argp->stable); |
1278 | complain = jiffies + 300 * HZ; | 1278 | complain = jiffies + 300 * HZ; |
1279 | } | 1279 | } |