aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManoj Naik <manoj@almaden.ibm.com>2006-06-09 09:34:29 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-06-09 09:34:29 -0400
commit6b97fd3da1eab2cc490cfe884c7d4956522eaf8b (patch)
tree055f5f603101a15f783ae52081e4714ddc3edc96
parent9cdb3883c38f883436a84c2353a4cf964ff890a2 (diff)
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>
-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