diff options
Diffstat (limited to 'fs/nfs/super.c')
-rw-r--r-- | fs/nfs/super.c | 1207 |
1 files changed, 444 insertions, 763 deletions
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 */ |