aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/inode.c5
-rw-r--r--fs/nfs/namespace.c9
-rw-r--r--fs/nfs/nfs4proc.c46
-rw-r--r--include/linux/nfs_fs.h1
-rw-r--r--include/linux/nfs_xdr.h1
5 files changed, 60 insertions, 2 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 0d8302e59d69..ee13cb01b56e 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -888,7 +888,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
888 set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); 888 set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
889 /* Deal with crossing mountpoints */ 889 /* Deal with crossing mountpoints */
890 if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { 890 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;
892 inode->i_fop = NULL; 895 inode->i_fop = NULL;
893 } 896 }
894 } else if (S_ISLNK(inode->i_mode)) 897 } else if (S_ISLNK(inode->i_mode))
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index e426516c1116..8ca44b7b25c3 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -58,7 +58,10 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
58 if (err != 0) 58 if (err != 0)
59 goto out_err; 59 goto out_err;
60 60
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);
62 err = PTR_ERR(mnt); 65 err = PTR_ERR(mnt);
63 if (IS_ERR(mnt)) 66 if (IS_ERR(mnt))
64 goto out_err; 67 goto out_err;
@@ -94,6 +97,10 @@ struct inode_operations nfs_mountpoint_inode_operations = {
94 .getattr = nfs_getattr, 97 .getattr = nfs_getattr,
95}; 98};
96 99
100struct inode_operations nfs_referral_inode_operations = {
101 .follow_link = nfs_follow_mountpoint,
102};
103
97static void nfs_expire_automounts(void *data) 104static void nfs_expire_automounts(void *data)
98{ 105{
99 struct list_head *list = (struct list_head *)data; 106 struct list_head *list = (struct list_head *)data;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 90ee21a07b3e..3300e35d74ad 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1462,6 +1462,50 @@ out:
1462 return nfs4_map_errors(status); 1462 return nfs4_map_errors(status);
1463} 1463}
1464 1464
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 */
1470static 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));
1501out:
1502 if (page)
1503 __free_page(page);
1504 if (locations)
1505 kfree(locations);
1506 return status;
1507}
1508
1465static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) 1509static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
1466{ 1510{
1467 struct nfs4_getattr_arg args = { 1511 struct nfs4_getattr_arg args = {
@@ -1566,6 +1610,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
1566 1610
1567 dprintk("NFS call lookup %s\n", name->name); 1611 dprintk("NFS call lookup %s\n", name->name);
1568 status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); 1612 status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
1613 if (status == -NFS4ERR_MOVED)
1614 status = nfs4_get_referral(dir, name, fattr, fhandle);
1569 dprintk("NFS reply lookup: %d\n", status); 1615 dprintk("NFS reply lookup: %d\n", status);
1570 return status; 1616 return status;
1571} 1617}
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 09271b10f9a8..152798949113 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -409,6 +409,7 @@ extern void nfs_unregister_sysctl(void);
409 */ 409 */
410extern struct list_head nfs_automount_list; 410extern struct list_head nfs_automount_list;
411extern struct inode_operations nfs_mountpoint_inode_operations; 411extern struct inode_operations nfs_mountpoint_inode_operations;
412extern struct inode_operations nfs_referral_inode_operations;
412extern int nfs_mountpoint_expiry_timeout; 413extern int nfs_mountpoint_expiry_timeout;
413extern void nfs_release_automount_timer(void); 414extern void nfs_release_automount_timer(void);
414 415
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index d6eea8348728..7c7320fa51aa 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -63,6 +63,7 @@ struct nfs_fattr {
63#define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ 63#define NFS_ATTR_FATTR 0x0002 /* post-op attributes */
64#define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ 64#define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */
65#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ 65#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */
66#define NFS_ATTR_FATTR_V4_REFERRAL 0x0010 /* NFSv4 referral */
66 67
67/* 68/*
68 * Info on the file system 69 * Info on the file system