Commit 6b97fd3da1eab2cc490cfe884c7d4956522eaf8b

Authored by Manoj Naik
Committed by Trond Myklebust
1 parent 9cdb3883c3

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>

Showing 5 changed files with 60 additions and 2 deletions Side-by-side Diff

... ... @@ -888,7 +888,10 @@
888 888 set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
889 889 /* Deal with crossing mountpoints */
890 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 895 inode->i_fop = NULL;
893 896 }
894 897 } else if (S_ISLNK(inode->i_mode))
... ... @@ -58,7 +58,10 @@
58 58 if (err != 0)
59 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 65 err = PTR_ERR(mnt);
63 66 if (IS_ERR(mnt))
64 67 goto out_err;
... ... @@ -92,6 +95,10 @@
92 95 struct inode_operations nfs_mountpoint_inode_operations = {
93 96 .follow_link = nfs_follow_mountpoint,
94 97 .getattr = nfs_getattr,
  98 +};
  99 +
  100 +struct inode_operations nfs_referral_inode_operations = {
  101 + .follow_link = nfs_follow_mountpoint,
95 102 };
96 103  
97 104 static void nfs_expire_automounts(void *data)
... ... @@ -1462,6 +1462,50 @@
1462 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 + */
  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 +
1465 1509 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
1466 1510 {
1467 1511 struct nfs4_getattr_arg args = {
... ... @@ -1566,6 +1610,8 @@
1566 1610  
1567 1611 dprintk("NFS call lookup %s\n", name->name);
1568 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 1615 dprintk("NFS reply lookup: %d\n", status);
1570 1616 return status;
1571 1617 }
include/linux/nfs_fs.h
... ... @@ -409,6 +409,7 @@
409 409 */
410 410 extern struct list_head nfs_automount_list;
411 411 extern struct inode_operations nfs_mountpoint_inode_operations;
  412 +extern struct inode_operations nfs_referral_inode_operations;
412 413 extern int nfs_mountpoint_expiry_timeout;
413 414 extern void nfs_release_automount_timer(void);
414 415  
include/linux/nfs_xdr.h
... ... @@ -63,6 +63,7 @@
63 63 #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */
64 64 #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */
65 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 69 * Info on the file system