Commit 6b97fd3d authored by Manoj Naik's avatar Manoj Naik Committed by Trond Myklebust

NFSv4: Follow a referral

Respond to a moved error on NFS lookup by setting up the referral.
Note: We don't actually follow the referral during lookup/getattr, but
later when we detect fsid mismatch in inode revalidation (similar to the
processing done for cloning submounts). Referrals will have fake attributes
until they are actually followed or traversed.
Signed-off-by: default avatarManoj Naik <manoj@almaden.ibm.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 9cdb3883
...@@ -888,7 +888,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -888,7 +888,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
/* Deal with crossing mountpoints */ /* Deal with crossing mountpoints */
if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
inode->i_op = &nfs_mountpoint_inode_operations; if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
inode->i_op = &nfs_referral_inode_operations;
else
inode->i_op = &nfs_mountpoint_inode_operations;
inode->i_fop = NULL; inode->i_fop = NULL;
} }
} else if (S_ISLNK(inode->i_mode)) } else if (S_ISLNK(inode->i_mode))
......
...@@ -58,7 +58,10 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -58,7 +58,10 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
if (err != 0) if (err != 0)
goto out_err; goto out_err;
mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL)
mnt = nfs_do_refmount(nd->mnt, nd->dentry);
else
mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
if (IS_ERR(mnt)) if (IS_ERR(mnt))
goto out_err; goto out_err;
...@@ -94,6 +97,10 @@ struct inode_operations nfs_mountpoint_inode_operations = { ...@@ -94,6 +97,10 @@ struct inode_operations nfs_mountpoint_inode_operations = {
.getattr = nfs_getattr, .getattr = nfs_getattr,
}; };
struct inode_operations nfs_referral_inode_operations = {
.follow_link = nfs_follow_mountpoint,
};
static void nfs_expire_automounts(void *data) static void nfs_expire_automounts(void *data)
{ {
struct list_head *list = (struct list_head *)data; struct list_head *list = (struct list_head *)data;
......
...@@ -1462,6 +1462,50 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -1462,6 +1462,50 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
return nfs4_map_errors(status); return nfs4_map_errors(status);
} }
/*
* Get locations and (maybe) other attributes of a referral.
* Note that we'll actually follow the referral later when
* we detect fsid mismatch in inode revalidation
*/
static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle)
{
int status = -ENOMEM;
struct page *page = NULL;
struct nfs4_fs_locations *locations = NULL;
struct dentry dentry = {};
page = alloc_page(GFP_KERNEL);
if (page == NULL)
goto out;
locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
if (locations == NULL)
goto out;
dentry.d_name.name = name->name;
dentry.d_name.len = name->len;
status = nfs4_proc_fs_locations(dir, &dentry, locations, page);
if (status != 0)
goto out;
/* Make sure server returned a different fsid for the referral */
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name);
status = -EIO;
goto out;
}
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
if (!fattr->mode)
fattr->mode = S_IFDIR;
memset(fhandle, 0, sizeof(struct nfs_fh));
out:
if (page)
__free_page(page);
if (locations)
kfree(locations);
return status;
}
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{ {
struct nfs4_getattr_arg args = { struct nfs4_getattr_arg args = {
...@@ -1566,6 +1610,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -1566,6 +1610,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
dprintk("NFS call lookup %s\n", name->name); dprintk("NFS call lookup %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == -NFS4ERR_MOVED)
status = nfs4_get_referral(dir, name, fattr, fhandle);
dprintk("NFS reply lookup: %d\n", status); dprintk("NFS reply lookup: %d\n", status);
return status; return status;
} }
......
...@@ -409,6 +409,7 @@ extern void nfs_unregister_sysctl(void); ...@@ -409,6 +409,7 @@ extern void nfs_unregister_sysctl(void);
*/ */
extern struct list_head nfs_automount_list; extern struct list_head nfs_automount_list;
extern struct inode_operations nfs_mountpoint_inode_operations; extern struct inode_operations nfs_mountpoint_inode_operations;
extern struct inode_operations nfs_referral_inode_operations;
extern int nfs_mountpoint_expiry_timeout; extern int nfs_mountpoint_expiry_timeout;
extern void nfs_release_automount_timer(void); extern void nfs_release_automount_timer(void);
......
...@@ -63,6 +63,7 @@ struct nfs_fattr { ...@@ -63,6 +63,7 @@ struct nfs_fattr {
#define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */
#define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */
#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ #define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */
#define NFS_ATTR_FATTR_V4_REFERRAL 0x0010 /* NFSv4 referral */
/* /*
* Info on the file system * Info on the file system
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment