Skip to content

Commit 6b97fd3

Browse files
Manoj NaikTrond Myklebust
authored andcommitted
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: Manoj Naik <manoj@almaden.ibm.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
1 parent 9cdb388 commit 6b97fd3

File tree

5 files changed

+60
-2
lines changed

5 files changed

+60
-2
lines changed

fs/nfs/inode.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
888888
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
889889
/* Deal with crossing mountpoints */
890890
if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
891-
inode->i_op = &nfs_mountpoint_inode_operations;
891+
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
892+
inode->i_op = &nfs_referral_inode_operations;
893+
else
894+
inode->i_op = &nfs_mountpoint_inode_operations;
892895
inode->i_fop = NULL;
893896
}
894897
} else if (S_ISLNK(inode->i_mode))

fs/nfs/namespace.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
5858
if (err != 0)
5959
goto out_err;
6060

61-
mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr);
61+
if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL)
62+
mnt = nfs_do_refmount(nd->mnt, nd->dentry);
63+
else
64+
mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr);
6265
err = PTR_ERR(mnt);
6366
if (IS_ERR(mnt))
6467
goto out_err;
@@ -94,6 +97,10 @@ struct inode_operations nfs_mountpoint_inode_operations = {
9497
.getattr = nfs_getattr,
9598
};
9699

100+
struct inode_operations nfs_referral_inode_operations = {
101+
.follow_link = nfs_follow_mountpoint,
102+
};
103+
97104
static void nfs_expire_automounts(void *data)
98105
{
99106
struct list_head *list = (struct list_head *)data;

fs/nfs/nfs4proc.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,50 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
14621462
return nfs4_map_errors(status);
14631463
}
14641464

1465+
/*
1466+
* Get locations and (maybe) other attributes of a referral.
1467+
* Note that we'll actually follow the referral later when
1468+
* we detect fsid mismatch in inode revalidation
1469+
*/
1470+
static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle)
1471+
{
1472+
int status = -ENOMEM;
1473+
struct page *page = NULL;
1474+
struct nfs4_fs_locations *locations = NULL;
1475+
struct dentry dentry = {};
1476+
1477+
page = alloc_page(GFP_KERNEL);
1478+
if (page == NULL)
1479+
goto out;
1480+
locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
1481+
if (locations == NULL)
1482+
goto out;
1483+
1484+
dentry.d_name.name = name->name;
1485+
dentry.d_name.len = name->len;
1486+
status = nfs4_proc_fs_locations(dir, &dentry, locations, page);
1487+
if (status != 0)
1488+
goto out;
1489+
/* Make sure server returned a different fsid for the referral */
1490+
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
1491+
dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name);
1492+
status = -EIO;
1493+
goto out;
1494+
}
1495+
1496+
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
1497+
fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
1498+
if (!fattr->mode)
1499+
fattr->mode = S_IFDIR;
1500+
memset(fhandle, 0, sizeof(struct nfs_fh));
1501+
out:
1502+
if (page)
1503+
__free_page(page);
1504+
if (locations)
1505+
kfree(locations);
1506+
return status;
1507+
}
1508+
14651509
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
14661510
{
14671511
struct nfs4_getattr_arg args = {
@@ -1566,6 +1610,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
15661610

15671611
dprintk("NFS call lookup %s\n", name->name);
15681612
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
1613+
if (status == -NFS4ERR_MOVED)
1614+
status = nfs4_get_referral(dir, name, fattr, fhandle);
15691615
dprintk("NFS reply lookup: %d\n", status);
15701616
return status;
15711617
}

include/linux/nfs_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ extern void nfs_unregister_sysctl(void);
409409
*/
410410
extern struct list_head nfs_automount_list;
411411
extern struct inode_operations nfs_mountpoint_inode_operations;
412+
extern struct inode_operations nfs_referral_inode_operations;
412413
extern int nfs_mountpoint_expiry_timeout;
413414
extern void nfs_release_automount_timer(void);
414415

include/linux/nfs_xdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct nfs_fattr {
6363
#define NFS_ATTR_FATTR 0x0002 /* post-op attributes */
6464
#define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */
6565
#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */
66+
#define NFS_ATTR_FATTR_V4_REFERRAL 0x0010 /* NFSv4 referral */
6667

6768
/*
6869
* Info on the file system

0 commit comments

Comments
 (0)