diff options
| -rw-r--r-- | fs/nfs/inode.c | 188 | ||||
| -rw-r--r-- | fs/nfs/nfs3proc.c | 2 | ||||
| -rw-r--r-- | include/linux/nfs_fs.h | 2 | ||||
| -rw-r--r-- | include/linux/nfs_fs_sb.h | 1 | ||||
| -rw-r--r-- | include/linux/nfs_mount.h | 6 |
5 files changed, 135 insertions, 64 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2bc4a618dd3f..837e7f3cb7e2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -151,15 +151,16 @@ void nfs_put_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); - struct rpc_clnt *rpc; #ifdef CONFIG_NFS_V4 if (server->idmap != NULL) nfs_idmap_delete(server); #endif /* CONFIG_NFS_V4 */ - if ((rpc = server->client) != NULL) - rpc_shutdown_client(rpc); + if (server->client != NULL) + rpc_shutdown_client(server->client); + if (server->client_sys != NULL) + rpc_shutdown_client(server->client_sys); if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ @@ -226,27 +227,57 @@ nfs_block_size(unsigned long bsize, unsigned char *nrbitsp) /* * Obtain the root inode of the file system. */ -static struct inode * -nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh) +static int +nfs_get_root(struct inode **rooti, rpc_authflavor_t authflavor, struct super_block *sb, struct nfs_fh *rootfh) { struct nfs_server *server = NFS_SB(sb); - struct nfs_fattr fattr; - struct inode *inode; + struct nfs_fattr fattr = { }; int error; - if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) { + error = server->rpc_ops->getroot(server, rootfh, &fattr); + if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { + /* + * Some authentication types (gss/krb5, most notably) + * are such that root won't be able to present a + * credential for GETATTR (ie, getroot()). + * + * We still want the mount to succeed. + * + * So we fake the attr values and mark the inode as such. + * On the first succesful traversal, we fix everything. + * The auth type test isn't quite correct, but whatever. + */ + dfprintk(VFS, "NFS: faking root inode\n"); + + fattr.fileid = 1; + fattr.nlink = 2; /* minimum for a dir */ + fattr.type = NFDIR; + fattr.mode = S_IFDIR|S_IRUGO|S_IXUGO; + fattr.size = 4096; + fattr.du.nfs3.used = 1; + fattr.valid = NFS_ATTR_FATTR|NFS_ATTR_FATTR_V3; + } else if (error < 0) { printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error); - return NULL; + *rooti = NULL; /* superfluous ... but safe */ + return error; } - inode = __nfs_fhget(sb, rootfh, &fattr); - return inode; + *rooti = __nfs_fhget(sb, rootfh, &fattr); + if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { + if (*rooti) { + NFS_FLAGS(*rooti) |= NFS_INO_FAKE_ROOT; + NFS_CACHEINV((*rooti)); + error = 0; + } + } + return error; } /* * Do NFS version-independent mount processing, and sanity checking */ -int nfs_sb_init(struct super_block *sb) +static int +nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) { struct nfs_server *server; struct inode *root_inode = NULL; @@ -267,8 +298,7 @@ int nfs_sb_init(struct super_block *sb) sb->s_op = &nfs_sops; /* Did getting the root inode fail? */ - root_inode = nfs_get_root(sb, &server->fh); - if (!root_inode) + if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) goto out_no_root; sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) @@ -346,19 +376,66 @@ out_no_root: } /* + * Create an RPC client handle. + */ +static struct rpc_clnt * +nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) +{ + struct rpc_timeout timeparms; + struct rpc_xprt *xprt = NULL; + struct rpc_clnt *clnt = NULL; + int tcp = (data->flags & NFS_MOUNT_TCP); + + /* Initialize timeout values */ + timeparms.to_initval = data->timeo * HZ / 10; + timeparms.to_retries = data->retrans; + timeparms.to_maxval = tcp ? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT; + timeparms.to_exponential = 1; + + if (!timeparms.to_initval) + timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10; + if (!timeparms.to_retries) + timeparms.to_retries = 5; + + /* create transport and client */ + xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP, + &server->addr, &timeparms); + if (xprt == NULL) { + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + goto out_fail; + } + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, data->pseudoflavor); + if (clnt == NULL) { + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); + goto out_fail; + } + + clnt->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; + clnt->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; + clnt->cl_droppriv = (server->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; + clnt->cl_chatty = 1; + + return clnt; + +out_fail: + if (xprt) + xprt_destroy(xprt); + return NULL; +} + +/* * The way this works is that the mount process passes a structure * in the data argument which contains the server's IP address * and the root file handle obtained from the server's mount * daemon. We stash these away in the private superblock fields. */ -int nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) +static int +nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) { struct nfs_server *server; - struct rpc_xprt *xprt = NULL; - struct rpc_clnt *clnt = NULL; - struct rpc_timeout timeparms; - int tcp, err = -EIO; - u32 authflavor; + int err = -EIO; + rpc_authflavor_t authflavor; server = NFS_SB(sb); sb->s_blocksize_bits = 0; @@ -400,46 +477,20 @@ int nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int sile server->rpc_ops = &nfs_v2_clientops; } - /* Which protocol do we use? */ - tcp = (data->flags & NFS_MOUNT_TCP); - - /* Initialize timeout values */ - timeparms.to_initval = data->timeo * HZ / 10; - timeparms.to_retries = data->retrans; - timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT; - timeparms.to_exponential = 1; - - if (!timeparms.to_initval) - timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10; - if (!timeparms.to_retries) - timeparms.to_retries = 5; - - /* Now create transport and client */ - xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, - &server->addr, &timeparms); - if (xprt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); - goto out_fail; - } - - if (data->flags & NFS_MOUNT_SECFLAVOUR) - authflavor = data->pseudoflavor; - else - authflavor = RPC_AUTH_UNIX; + /* Fill in pseudoflavor for mount version < 5 */ + if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) + data->pseudoflavor = RPC_AUTH_UNIX; + authflavor = data->pseudoflavor; /* save for sb_init() */ + /* XXX maybe we want to add a server->pseudoflavor field */ - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, authflavor); - if (clnt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); + /* Create RPC client handles */ + server->client = nfs_create_client(server, data); + if (server->client == NULL) goto out_fail; - } - - clnt->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; - clnt->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; - clnt->cl_droppriv = (server->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; - clnt->cl_chatty = 1; - server->client = clnt; + data->pseudoflavor = RPC_AUTH_UNIX; /* RFC 2623, sec 2.3.2 */ + server->client_sys = nfs_create_client(server, data); + if (server->client_sys == NULL) + goto out_shutdown; /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) { @@ -447,7 +498,7 @@ int nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int sile goto out_shutdown; } - err = nfs_sb_init(sb); + err = nfs_sb_init(sb, authflavor); if (err != 0) goto out_noinit; @@ -466,7 +517,10 @@ int nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int sile out_noinit: rpciod_down(); out_shutdown: - rpc_shutdown_client(server->client); + if (server->client) + rpc_shutdown_client(server->client); + if (server->client_sys) + rpc_shutdown_client(server->client_sys); out_fail: if (server->hostname) kfree(server->hostname); @@ -904,6 +958,11 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) goto out_nowait; if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode) goto out_nowait; + if (NFS_FAKE_ROOT(inode)) { + dfprintk(VFS, "NFS: not revalidating fake root\n"); + status = 0; + goto out_nowait; + } while (NFS_REVALIDATING(inode)) { status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); @@ -1007,6 +1066,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_sb->s_id, inode->i_ino, atomic_read(&inode->i_count), fattr->valid); + /* First successful call after mount, fill real data. */ + if (NFS_FAKE_ROOT(inode)) { + dfprintk(VFS, "NFS: updating fake root\n"); + nfsi->fileid = fattr->fileid; + NFS_FLAGS(inode) &= ~NFS_INO_FAKE_ROOT; + } + if (nfsi->fileid != fattr->fileid) { printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n" "expected (%s/0x%Lx), got (%s/0x%Lx)\n", @@ -1229,6 +1295,8 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, root->size = NFS2_FHSIZE; memcpy(root->data, data->old_root.data, NFS2_FHSIZE); } + if (data->version < 5) + data->flags &= ~NFS_MOUNT_SECFLAVOUR; } if (root->size > sizeof(root->data)) { diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index b9c01c0c5139..334ead4e4dd3 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -681,7 +681,7 @@ nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, dprintk("NFS call fsinfo\n"); info->fattr->valid = 0; - status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0); + status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0); dprintk("NFS reply fsinfo: %d\n", status); return status; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a6d594bb252c..be2bfde99044 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -172,6 +172,7 @@ struct nfs_inode { #define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ #define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ #define NFS_INO_FLUSH 0x0008 /* inode is due for flushing */ +#define NFS_INO_FAKE_ROOT 0x0080 /* root inode placeholder */ static inline struct nfs_inode *NFS_I(struct inode *inode) { @@ -207,6 +208,7 @@ do { \ #define NFS_FLAGS(inode) (NFS_I(inode)->flags) #define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) #define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE) +#define NFS_FAKE_ROOT(inode) (NFS_FLAGS(inode) & NFS_INO_FAKE_ROOT) #define NFS_FILEID(inode) (NFS_I(inode)->fileid) diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 9cf1491f5ff9..20ceb626cb3b 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -9,6 +9,7 @@ */ struct nfs_server { struct rpc_clnt * client; /* RPC client handle */ + struct rpc_clnt * client_sys; /* 2nd handle for FSINFO */ struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */ struct backing_dev_info backing_dev_info; int flags; /* various flags */ diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h index c2268ca1ccd3..23a9ff52537f 100644 --- a/include/linux/nfs_mount.h +++ b/include/linux/nfs_mount.h @@ -20,7 +20,7 @@ * mount-to-kernel version compatibility. Some of these aren't used yet * but here they are anyway. */ -#define NFS_MOUNT_VERSION 4 +#define NFS_MOUNT_VERSION 5 struct nfs_mount_data { int version; /* 1 */ @@ -40,7 +40,7 @@ struct nfs_mount_data { int namlen; /* 2 */ unsigned int bsize; /* 3 */ struct nfs3_fh root; /* 4 */ - int pseudoflavor; /* 4 */ + int pseudoflavor; /* 5 */ }; /* bits in the flags field */ @@ -57,7 +57,7 @@ struct nfs_mount_data { #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */ #define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */ -#define NFS_MOUNT_SECFLAVOUR 0x2000 /* reserved */ +#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */ #define NFS_MOUNT_FLAGMASK 0xFFFF #endif |
