aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
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 /fs/nfs
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>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/inode.c5
-rw-r--r--fs/nfs/namespace.c9
-rw-r--r--fs/nfs/nfs4proc.c46
3 files changed, 58 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}